news 2025/12/20 18:13:26

告别手动引入依赖:unplugin-auto-import 插件助你提升编码体验(内附实现原理)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
告别手动引入依赖:unplugin-auto-import 插件助你提升编码体验(内附实现原理)

目录

使用效果

使用效果

使用方法

基本使用

使用预设

编码问题

TS 类型

Eslint

实现原理

unimport

测试用例

如何注入 import 语句

初始化预设

扫描和注入

实战自动引入组件库

特性

自动引入

安装插件

Vite

Vue CLI

Typescript 配置注意

手动引入

手动引入和自动引入对比

对比-手动引入

对比-自动引入

完整配置

总结


模块化已经是现代 Web 开发必不可少的开发方式,频繁引入依赖包是一个常见的操作。但是,手动引入依赖包往往繁琐,尤其是当依赖包数量较多时,会显著降低开发效率。

unplugin-auto-import插件,可以帮助我们在项目中,自动导入常用的使用的第三方库的 API,就可以方便我们开发,提升开发效率。

使用效果

以 Vue 为例,在没有使用自动导入前,需要手写以下的import语句:

import { computed, ref } from 'vue' const count = ref(0) const doubled = computed(() => count.value * 2)

使用效果

以 Vue 为例,在没有使用自动导入前,需要手写以下的import语句:

import { computed, ref } from 'vue' const count = ref(0) const doubled = computed(() => count.value * 2)

使用unplugin-auto-import插件后:

const count = ref(0) const doubled = computed(() => count.value * 2)

使用方法

基本使用

unplugin-auto-import是基于 unplugin 写的,支持 Vite、Webpack、Rollup、esbuild 多个打包工具。

vite 的使用方式如下:

// vite.config.ts import AutoImport from 'unplugin-auto-import/vite' export default defineConfig({ plugins: [ AutoImport({ imports:[ // 预设 ], }), ], })

使用预设

unplugin-auto-import插件一般配合预设进行使用,预设负责告诉插件应该自动引入哪些内容

目前支持:

  • Vue
  • vue-router
  • @vueuse/core
  • react
  • react-router
  • ……,更多请查看这里

预设的配置方式

AutoImport({ imports [ // 预设 'vue', 'vue-router', // 自定义预设 { '@vueuse/core': [ // 命名导入 'useMouse', // import { useMouse } from '@vueuse/core', // 设置别名 ['useFetch', 'useMyFetch'], // import { useFetch as useMyFetch } from '@vueuse/core', ], 'axios': [ // 默认导入 ['default', 'axios'], // import { default as axios } from 'axios', ], '[package-name]': [ '[import-names]', // alias ['[from]', '[alias]'], ], }, // example type import { from: 'vue-router', imports: ['RouteLocationRaw'], type: true, }, ], })

有多种方式设置预设:

  • 字符串语法,最终会被转换成内置的预设(对象语法写的)
  • 对象语法
    • key 为包名
    • value 为数组,对应的是各个自动引入的变量的名称。同时可以设置引入方式(命名导入/默认导入),

对于Typescript 类型的自动引入,则需要用以下方式:

{ from: 'vue-router', imports: ['RouteLocationRaw'], type: true },

我们来看看 Vue 的预设是怎么写的,完整代码在这里,下面是节选的代码:

export const CommonCompositionAPI: InlinePreset['imports'] = [ // 声明周期,节选 'onActivated', 'onBeforeMount', // reactivity,节选 'computed', 'ref', 'watch', // 组件 API,节选 'defineComponent', 'h', 'inject', 'nextTick', // Typescript 类型,接续那 ...[ 'Component', 'Ref', 'VNode' ].map(name => ({ name, type: true })) ] export default defineUnimportPreset({ from: 'vue', imports: [ ...CommonCompositionAPI, ] })

Vue 预设里,本质就是使用对象语法,定义了 Vue 需要被自动导入的内容。

编码问题

要想在项目中优雅地使用自动导入,还要解决以下两个编码的问题:

  • TS 类型丢失,会导致 TS 编译报错
  • Eslint 报错:变量未定义

TS 类型

如果使用 Typescript,需要设置dts为 true

AutoImport({ dts: true // or a custom path })

插件会在项目根目录生成类型文件auto-imports.d.ts,确保该文件在tsconfig中被include

auto-imports.d.ts有什么作用?

我们来看看它的内容(有节选):

export {} declare global { const h: typeof import('vue')['h'] const reactive: typeof import('vue')['reactive'] const ref: typeof import('vue')['ref'] const watch: typeof import('vue')['watch'] const watchEffect: typeof import('vue')['watchEffect'] // 省略其他内容 }

unplugin-auto-import插件会根据预设内容,生成对应的全局类型声明

有了这些全局类型声明,我们就能够像全局变量那样使用ref等 Vue API,不需要先import对应的内容,TS 编译也不会报错。

Eslint

如果使用了 eslint,需要设置eslintrc字段

AutoImport({ eslintrc: { enabled: true, }, })

插件会在项目根目录生成类型文件.eslintrc-auto-import.json,确保该文件在eslint配置中被extends

// .eslintrc.js module.exports = { extends: [ './.eslintrc-auto-import.json', ], }

.eslintrc-auto-import.json有什么作用?

我们来看看它的内容(有节选):

{ "globals": { "h": true, "reactive": true, "ref": true, "watch": true, "watchEffect": true, } }

unplugin-auto-import插件会根据预设内容,生成对应的 eslint 配置文件,该文件定义了href这些为全局变量,不需要引入就能直接使用。这样 ESlint 就不会报变量没有定义的错误了。

实现原理

v0.8.0来开始,unplugin-auto-import基于unimport开发,所有的转换能力,都是unimport提供的,unplugin-auto-import可以理解成为一个提供了更友好的 API 和功能的包装层。基本上所有新功能都会在unimport中开发。

那核心的实现,我们直接去看unimport就好了。

eslint 配置的生成是由unplugin-auto-import提供

unimport

我们直接看看插件代码

export const defaultIncludes = [/\.[jt]sx?$/, /\.vue$/, /\.vue\?vue/, /\.svelte$/] export const defaultExcludes = [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/] export default createUnplugin<Partial<UnimportPluginOptions>>((options = {}) => { const ctx = createUnimport(options) const filter = createFilter( toArray(options.include as string[] || []).length ? options.include : defaultIncludes, options.exclude || defaultExcludes ) const dts = options.dts === true ? 'unimport.d.ts' : options.dts return { name: 'unimport', // 在用户插件执行完之后执行 enforce: 'post', // 过滤文件,默认只处理 、js、jsx、ts、tsx、vue、svelte 文件 // 默认排除 node_modules 下的文件 transformInclude (id) { return filter(id) }, // 转换文件逻辑 async transform (code, id) { const s = new MagicString(code) // 注入 import 语句 await ctx.injectImports(s, id) if (!s.hasChanged()) { return } return { code: s.toString(), map: s.generateMap() } }, // 构建开始时,生成 ts 类型声明文件 async buildStart () { await ctx.init() // 生成 Typescript 全局类型声明 if (options.dts) { return fs.writeFile(dts, await ctx.generateTypeDeclarations(), 'utf-8') } } } })

插件用基于unplugin写的,用unplugin写的插件,能用在 Vite、Webpack、Rollup、esbuild 多个打包工具,即**unplugin抹平了打包工具间的一些差异**。

unimport插件主要的处理逻辑如下:

  1. 过滤出需要处理的文件,对文件进行转换,注入 import 语句
  2. 生成 ts类型声明文件

unimport为什么需要在其他插件后执行?

因为有些代码需要先经过处理,才会变成 js,例如 Vue 文件。

测试用例

我们直接使用unimport提供的示例,其中一个文件为:

import { Ref } from 'vue' export const multiplier = ref(2) export function useDoubled (v: Ref<number>) { return computed(() => v.value * multiplier.value) } export function bump () { multiplier.value += 1 } const localA = 'localA' const localB = 'localB' export { localA, localB as localBAlias }

我们通过vite-plugin-inspect插件,可以看到该文件被转换的过程:

  1. esbuild转换

  1. unimport插件转换

可以看出unbuild插件自动加入了 import 语句

  1. import-analysis插件处理

将 Vue 改为一个可以访问的路径,让 vue 的相关文件在 dev 环境下能够被正常访问到。

如何注入 import 语句

注入 import 语句,是unimport的核心逻辑,主要有以下几个步骤:

  1. 初始化预设
  2. 扫描文件
  3. 注入 import

初始化预设

  1. 将字符串的内置预设,标准化为对象语法
  2. 将所有配置对象合并成一个importMap对象

importMap 数据结构如下:

{ reactive: { from: 'vue, name: 'reactive' as: 'reactive' }, // 转换成 import { reactive } from 'vue' ref: { from: 'vue, name: 'ref as: 'ref }, // 转换成 import { ref } from 'vue' // …… }

有了importMap对象,就可以快速判断一个标志符,是否需要转换了

扫描和注入

  1. 查找所有可能需要注入的标志符
import { Ref } from 'vue' export const multiplier = ref(2) export function useDoubled (v: Ref<number>) { return computed(() => v.value * multiplier.value) } export function bump () { multiplier.value += 1 } const localA = 'localA' const localB = 'localB' export { localA, localB as localBAlias }

以上述文件为例,从中找出没有先定义再使用的标志符,排除 js 关键字(function 等)

可以查找到有以下的标志符,未被定义却使用了

  • ref
  • computed

并且这两个标志符都importMap中能找到,这标明这两个标志符,需要注入 import

因此会注入以下代码:

import { ref, computed } from 'vue';

实战自动引入组件库

@varlet/import-resolver是 unplugin-vue-components 的一个解析器,用于实现 Varlet 按需引入。

特性

  • 支持ViteWebpackVue CLIRollupesbuild
  • 支持自动引入组件对应的 CSS 样式
  • 支持 SSR(服务端渲染)

自动引入

通过插件 unplugin-vue-components 和 unplugin-auto-import 实现组件自动按需导入,这也是我们最推荐的方式。

安装插件

# npm npm i @varlet/import-resolver unplugin-vue-components unplugin-auto-import -D # yarn yarn add @varlet/import-resolver unplugin-vue-components unplugin-auto-import -D # pnpm pnpm add @varlet/import-resolver unplugin-vue-components unplugin-auto-import -D

Vite

// vite.config.js import vue from '@vitejs/plugin-vue' import components from 'unplugin-vue-components/vite' import autoImport from 'unplugin-auto-import/vite' import { VarletImportResolver } from '@varlet/import-resolver' import { defineConfig } from 'vite' export default defineConfig({ plugins: [ vue(), components({ resolvers: [VarletImportResolver()] }), autoImport({ resolvers: [VarletImportResolver({ autoImport: true })] }) ] })

Vue CLI

// vue.config.js const Components = require('unplugin-vue-components/webpack') const AutoImport = require('unplugin-auto-import/webpack') const { VarletImportResolver } = require('@varlet/import-resolver') module.exports = { configureWebpack: { plugins: [ Components.default({ resolvers: [VarletImportResolver()] }), AutoImport.default({ resolvers: [VarletImportResolver({ autoImport: true })] }) ] } }

Typescript 配置注意

为了得到良好的 IDE 语法高亮,请确保上述两个插件生成的类型声明文件被typescript识别,可在tsconfig.json中进行如下配置:

{ "include": ["auto-imports.d.ts", "components.d.ts"] }

手动引入

每一个组件都是一个Vue插件,并由组件逻辑样式文件组成,手动引入的使用方式如下。

import App from './App.vue' import { createApp } from 'vue' import { Button } from '@varlet/ui' import '@varlet/ui/es/button/style/index' createApp(App).use(Button)

<script setup> import { Button as VarButton } from '@varlet/ui' import '@varlet/ui/es/button/style/index' </script> <template> <var-button>说你好</var-button> </template>

手动引入和自动引入对比

对比-手动引入
<script setup> import { Button as VarButton, Snackbar } from '@varlet/ui' import '@varlet/ui/es/button/style/index' import '@varlet/ui/es/snackbar/style/index' function handleClick() { Snackbar('你好!') } </script> <template> <var-button @click="handleClick">说你好</var-button> </template>
对比-自动引入
<script setup> function handleClick() { Snackbar('你好!') } </script> <template> <var-button @click="handleClick">说你好</var-button> </template>

完整配置

/** * @name ConfigAutoImportPlugin * @description 按需加载,自动引入 */ import AutoImport from 'unplugin-auto-import/vite'; import { VarletImportResolver } from '@varlet/import-resolver'; import { VantResolver } from '@vant/auto-import-resolver'; export const ConfigAutoImportPlugin = () => { return AutoImport({ dts: 'types/auto-imports.d.ts', imports: [ 'vue', 'pinia', 'vue-router', { '@vueuse/core': [], }, ], eslintrc: { enabled: true, }, resolvers: [VarletImportResolver({ autoImport: true }), VantResolver()], }); };

总结

并非所以依赖都适合自动导入,项目内的代码可能就不一定适合自动引入

因为自动引入后,就能像全局变量那样直接使用,但从开发的角度就会丢失依赖链路,虽然另外生成了 Typescript 声明文件,IDE 能够正常识别, 但对于新加入项目的同学来说,他们不一定知道是自动引入,因此可能会降低了一些可读性。

因此我们要有权衡。

那么,什么样的内容适合自动引入?被广泛认知和使用、不用关注实现、不变的内容

这些内容不关注实现,不会影响可读性,不会影响开发,不会对开发者心智造成影响。

这类内容,就适合自动引入。例如我们例子中的 Vue composition API,就已经成为一种 Vue 开发者共识了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/12 1:28:55

2026年全套Java面试合集,终于整理完了!

一、Java并发面试题 1、 ThreadLocal 1.1 谈谈你对ThreadLocal的理解&#xff1f; ThreadLocal的作用主要是做数据隔离&#xff0c;填充的数据只属于当前线程&#xff0c;变量的数据对别的线程而言是相对隔离的。它不是针对程序的全局变量&#xff0c;只是针对当前线程的全局…

作者头像 李华
网站建设 2025/12/12 1:27:42

数字孪生技术有哪些实际应用?

数字孪生&#xff0c;作为一种将物理实体或系统在其全生命周期内&#xff0c;通过数据驱动在虚拟空间中构建动态镜像的技术&#xff0c;正深刻改变着众多行业的运作模式。它不仅是一个简单的三维模型&#xff0c;更是一个集成了实时数据、模拟分析、预测决策能力的综合系统。随…

作者头像 李华
网站建设 2025/12/12 1:24:16

我为什么要离开家乡,来北京打拼?(说说我自己的故事...)

建了一个新号&#xff1a;1. 讲职场与第二曲线&#xff1b;2. 聊自己的故事&#xff0c;内心的感悟。谢谢大家&#xff0c;听我的故事。希望对大伙也有帮助。最近做了一个新产品&#xff1a;70天&#xff0c;每天30分钟&#xff0c;短视频行动营&#xff08;第二曲线最佳选择&a…

作者头像 李华
网站建设 2025/12/14 8:28:36

如何在 LTspice放置 .op data 并能够设置显示的小数点个数?

简 介&#xff1a; 本文介绍了在LTspice中格式化.op数据标签的方法。通过使用round函数可以设置显示数据的小数点位数&#xff0c;使仿真结果更加简洁直观。具体操作是右键点击.op数据标签&#xff0c;使用round函数调整小数位数。这种方法能有效优化电路静态偏置量的显示效果&…

作者头像 李华
网站建设 2025/12/12 1:20:56

Wan2.2-T2V-A14B支持长时间序列生成吗?实测60秒连续输出

Wan2.2-T2V-A14B支持长时间序列生成吗&#xff1f;实测60秒连续输出 在影视制作、广告创意和虚拟内容生产领域&#xff0c;一个长期悬而未决的难题是&#xff1a;AI能否真正理解“时间”&#xff1f; 不是简单拼接几帧画面&#xff0c;也不是靠后期插值强行延长视频&#xff…

作者头像 李华