Skip to content

从 0 实现“自动导入 API”功能的 Vite 插件,核心思想与 unplugin-auto-import 一样。

🎯 最终效果示例(插件做到什么)

例如你在 Vue 或 React 项目里写代码:

ts
const state = ref(0)
watch(state, () => {})
onMounted(() => {})

即使你没有写:

ts
import { ref, watch, onMounted } from 'vue'

插件也会在编译阶段自动帮你加上。

最终编译变成:

ts
import { ref, watch, onMounted } from 'vue'

const state = ref(0)
watch(state, () => {})
onMounted(() => {})

🧠 插件核心原理

Vite 的 transform() hook 能拿到文件源码:

transform(code, id)

我们做三件事:

① 扫描代码:找出哪些 API 被使用

例如 ref / computed / watch 等

② 判断这些 API 来自哪个库(vue/react/vueuse 等)

③ 自动生成 import 语句 + prepend 在文件最前面

🚀 Step 1:创建插件文件

创建 plugins/auto-import.js

🚀 Step 2:写一个最简自动导入插件

下面是一个 可运行、核心版、支持 Vue 的自动导入插件

js
export default function autoImport(options = {}) {
  const imports = options.imports || {
    vue: ['ref', 'reactive', 'computed', 'watch', 'onMounted']
  }

  return {
    name: 'vite-plugin-auto-import',

    transform(code, id) {
      // 只处理 JS/TS/Vue 的 script 部分
      if (!/\.(js|ts|vue)$/.test(id)) return

      const importStatements = []
      const usedApis = new Set()

      // 遍历 imports 配置
      for (const pkg in imports) {
        const apis = imports[pkg]
        apis.forEach(api => {
          if (code.includes(api)) {
            usedApis.add(api)
          }
        })
      }

      if (usedApis.size === 0) return

      // 根据使用的 API 生成 import 语句
      for (const pkg in imports) {
        const apis = imports[pkg].filter(api => usedApis.has(api))
        if (apis.length > 0) {
          importStatements.push(
            `import { ${apis.join(', ')} } from '${pkg}';`
          )
        }
      }

      const importCode = importStatements.join('\n')

      // 将 import 代码插入到文件顶部
      return {
        code: importCode + '\n' + code,
        map: null,
      }
    }
  }
}

🚀 Step 3:在 Vite 中使用插件

vite.config.js

js
import AutoImport from './plugins/auto-import.js'

export default {
  plugins: [
    AutoImport({
      imports: {
        vue: ['ref', 'reactive', 'computed', 'watch', 'onMounted'],
        'vue-router': ['useRoute', 'useRouter']
      }
    })
  ]
}

✨ 现在,你已经拥有了一个真正能工作的自动导入插件!

继续我们进入进阶增强版本。

🧠 Step 4:处理 Vue SFC(只编译 <script> 内容)

vue 单文件组件可能还有 <template> 等。

我们只想处理 <script>

js
if (id.endsWith('.vue')) {
  const script = code.match(/<script[^>]*>([\s\S]*)<\/script>/)
  if (!script) return
  code = script[1]
}

🧠 Step 5:避免重复导入(真正专业)

如果用户已经手动写了:

ts
import { ref } from 'vue'

我们要避免重复添加。

只需检查代码里是否已有该 import:

js
if (code.includes(`from '${pkg}'`) && code.includes(api)) {
  return
}

增强版本(示例):

js
const alreadyImported = code.includes(`import {`)

可做更强的 AST 检测(高级版)。

🧠 Step 6:通过 AST 提升精确度

使用 @babel/parser 解析抽象语法树:

bash
npm i @babel/parser @babel/traverse

示例:

js
import parser from '@babel/parser'
import traverse from '@babel/traverse'

const ast = parser.parse(code, { sourceType: 'module' })

然后检测:

js
traverse.default(ast, {
  Identifier(path) {
    const name = path.node.name
    usedApis.add(name)
  }
})

这样就不会误判字符串内容。

🧠 Step 7:构建完整插件

以下是一个 专业级、可扩展、精确识别 API、带 AST 的自动导入插件 结构:

auto-import/
 ├─ index.js
 ├─ utils/scan.js
 ├─ utils/generate.js
 ├─ package.json

核心 index.js 会负责:

  • 读取用户配置
  • 分析源码
  • 使用 AST 找 used identifiers
  • 自动生成 import
  • 支持 Vue 文件
  • 支持多包映射
  • 支持 dts(自动生成声明文件)

如:

ts
// auto-import.d.ts
declare const ref: typeof import('vue')['ref']

你完全可以继续扩展。