news 2026/6/10 0:27:18

告别手动引入依赖: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进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 9:47:21

【企业级Agent安全配置】:Docker环境下99%的人都忽略的5大安全隐患

第一章&#xff1a;企业级Agent安全配置的核心挑战在现代分布式系统架构中&#xff0c;Agent作为连接终端节点与中央管理平台的关键组件&#xff0c;承担着数据采集、指令执行和状态上报等核心职责。然而&#xff0c;随着攻击面的不断扩展&#xff0c;企业级Agent的安全配置面临…

作者头像 李华
网站建设 2026/6/10 13:15:23

Comsol 超构表面远场偏振态绘制那些事儿

Comsol绘制超构表面远场偏振态 动量空间远场偏振far field polarization 绘制教程。 C点 V点识别 Comsol 超构表面动量空间参数图绘制在超构表面的研究领域中&#xff0c;利用 Comsol 绘制远场偏振态以及动量空间相关参数图是非常重要的工作。今天就来跟大家唠唠这其中的门道…

作者头像 李华
网站建设 2026/6/10 11:18:55

1.19 UGUI的准备数据流程

1.UGUI准备数据的流程a.PostLateUpdate.PlayerUpdateCanvases- 作用: 这是Unity在每帧的晚期更新(LateUpdate)之后, 专门用于更新所有Canvas(UI画布)的系统函数; 它负责驱动整个UI渲染流程- 详细流程: 它会调用Canvas.SendWillRenderCanvases(), 从而触发一系列UI更新操作, 包括…

作者头像 李华
网站建设 2026/6/10 12:52:15

MCP SC-400量子加密实战指南(从零到企业级安全架构)

第一章&#xff1a;MCP SC-400量子安全配置实务概述在当前量子计算快速发展的背景下&#xff0c;传统加密体系面临前所未有的破解风险。MCP SC-400作为新一代量子安全通信协议标准&#xff0c;旨在提供抗量子攻击的安全配置框架&#xff0c;保障关键基础设施与敏感数据的长期安…

作者头像 李华
网站建设 2026/6/10 16:20:16

JavaEE进阶——MyBatis动态SQL与图书管理系统实战

目录 MyBatis 进阶详解与图书管理系统实战 第一部分&#xff1a;核心知识点深度解析 1. 什么是动态 SQL&#xff1f;为什么需要它&#xff1f; 2. 动态 SQL 标签详解&#xff08;文档核心点扩展&#xff09; 2.1 标签&#xff1a;最常用的判断逻辑2.2 标签&#xff1a;万能…

作者头像 李华
网站建设 2026/6/10 12:52:58

初始网络原理

理论知识网络的发展历程单机时代->局域网->广域网->移动互联网时代(国内的网络发展相对较慢,2000年前后,才真正的进入了网络时代)组建网络的核心设备路由器和交换机是组建网络的核心设备(交换机可以认为是对路由器的接口进行拓展)网络通信基础知识网络互联的目的是为了…

作者头像 李华