自动导入
自动导入 Vue 组件和 API,无需手动 import,提升开发效率。
unplugin-auto-import
基本信息
- 简介: 自动导入 Vue、React 等框架的 API 和工具函数
- 链接: https://github.com/unplugin/unplugin-auto-import
- GitHub: https://github.com/unplugin/unplugin-auto-import
- npm: https://www.npmjs.com/package/unplugin-auto-import
特点
- ✅ 支持 Vite、Webpack、Rollup、esbuild 等多种构建工具
- ✅ 自动导入 Vue 3 Composition API(ref、computed、watch 等)
- ✅ 支持 Vue Router、Pinia 等生态库
- ✅ 自动生成 TypeScript 类型声明
- ✅ 支持自定义导入规则
- ✅ 按需导入,不影响打包体积
- ✅ 完善的 IDE 智能提示支持
安装
bash
pnpm add -D unplugin-auto-importbash
bun add -D unplugin-auto-importbash
npm install -D unplugin-auto-importbash
yarn add -D unplugin-auto-importVite 配置
typescript
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig({
plugins: [
vue(),
AutoImport({
// 自动导入 Vue 相关函数
imports: ['vue', 'vue-router', 'pinia'],
// 生成类型声明文件
dts: 'src/auto-imports.d.ts',
// ESLint 支持
eslintrc: {
enabled: true,
filepath: './.eslintrc-auto-import.json',
globalsPropValue: true
}
})
]
})基础用法
配置完成后,无需手动导入即可直接使用 Vue API:
vue
<script setup lang="ts">
// 无需 import { ref, computed, watch } from 'vue'
// 直接使用
const count = ref(0)
const doubled = computed(() => count.value * 2)
watch(count, (newVal) => {
console.log('count changed:', newVal)
})
const increment = () => {
count.value++
}
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<p>Doubled: {{ doubled }}</p>
<button @click="increment">增加</button>
</div>
</template>Vue Router 自动导入
typescript
// vite.config.ts
AutoImport({
imports: [
'vue',
'vue-router'
],
dts: 'src/auto-imports.d.ts'
})使用示例:
vue
<script setup lang="ts">
// 无需 import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
const goToHome = () => {
router.push('/')
}
// 获取路由参数
const id = computed(() => route.params.id)
</script>
<template>
<div>
<p>当前路由: {{ route.path }}</p>
<p>参数 ID: {{ id }}</p>
<button @click="goToHome">返回首页</button>
</div>
</template>Pinia 自动导入
typescript
// vite.config.ts
AutoImport({
imports: [
'vue',
'pinia'
],
dts: 'src/auto-imports.d.ts'
})使用示例:
vue
<script setup lang="ts">
// 无需 import { storeToRefs } from 'pinia'
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
const { username, isLoggedIn } = storeToRefs(userStore)
const login = () => {
userStore.login('admin', 'password')
}
</script>
<template>
<div>
<p v-if="isLoggedIn">欢迎, {{ username }}</p>
<button v-else @click="login">登录</button>
</div>
</template>自定义导入
typescript
// vite.config.ts
AutoImport({
imports: [
'vue',
{
// 从 axios 导入
'axios': [
['default', 'axios'] // import axios from 'axios'
],
// 从自定义工具库导入
'@/utils/request': [
'request',
'get',
'post'
],
// 从 lodash-es 导入
'lodash-es': [
'debounce',
'throttle',
'cloneDeep'
]
}
],
dts: 'src/auto-imports.d.ts'
})使用示例:
vue
<script setup lang="ts">
// 无需手动导入,直接使用
const searchQuery = ref('')
// 使用 lodash 的 debounce
const debouncedSearch = debounce((query: string) => {
console.log('搜索:', query)
// 使用自定义的 request 函数
get('/api/search', { params: { q: query } })
}, 300)
watch(searchQuery, (newVal) => {
debouncedSearch(newVal)
})
</script>
<template>
<input v-model="searchQuery" placeholder="搜索..." />
</template>目录自动导入
自动导入指定目录下的所有函数:
typescript
// vite.config.ts
AutoImport({
imports: ['vue'],
// 自动导入 src/composables 目录下的所有文件
dirs: [
'./src/composables',
'./src/utils'
],
dts: 'src/auto-imports.d.ts'
})目录结构:
src/
composables/
useCounter.ts
useUser.ts
utils/
format.ts使用示例:
vue
<script setup lang="ts">
// 无需导入,直接使用 composables 中的函数
const { count, increment, decrement } = useCounter()
const { user, login, logout } = useUser()
// 直接使用 utils 中的函数
const formattedDate = formatDate(new Date())
</script>VueUse 集成
typescript
// vite.config.ts
AutoImport({
imports: [
'vue',
'@vueuse/core'
],
dts: 'src/auto-imports.d.ts'
})使用示例:
vue
<script setup lang="ts">
// 无需 import { useMouse, useLocalStorage } from '@vueuse/core'
// 直接使用 VueUse 的函数
const { x, y } = useMouse()
const counter = useLocalStorage('counter', 0)
const isDark = useDark()
const toggleDark = useToggle(isDark)
</script>
<template>
<div>
<p>鼠标位置: {{ x }}, {{ y }}</p>
<p>计数器: {{ counter }}</p>
<button @click="counter++">增加</button>
<button @click="toggleDark()">切换主题</button>
</div>
</template>unplugin-vue-components
基本信息
- 简介: 自动导入 Vue 组件,无需手动注册
- 链接: https://github.com/unplugin/unplugin-vue-components
- GitHub: https://github.com/unplugin/unplugin-vue-components
- npm: https://www.npmjs.com/package/unplugin-vue-components
特点
- ✅ 自动导入组件,无需手动注册
- ✅ 支持多种 UI 库(Element Plus、Ant Design Vue、Naive UI 等)
- ✅ 按需加载,优化打包体积
- ✅ 自动生成 TypeScript 类型声明
- ✅ 支持自定义组件目录
- ✅ 完善的 Tree-shaking 支持
- ✅ 支持 Vite、Webpack、Rollup 等构建工具
安装
bash
pnpm add -D unplugin-vue-componentsbash
bun add -D unplugin-vue-componentsbash
npm install -D unplugin-vue-componentsbash
yarn add -D unplugin-vue-componentsVite 配置
typescript
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
export default defineConfig({
plugins: [
vue(),
Components({
// 自动导入 src/components 目录下的组件
dirs: ['src/components'],
// 组件的有效文件扩展名
extensions: ['vue'],
// 生成类型声明文件
dts: 'src/components.d.ts'
})
]
})基础用法
配置完成后,无需手动导入和注册组件:
目录结构:
src/
components/
Button.vue
Card.vue
Header.vue直接使用:
vue
<script setup lang="ts">
// 无需导入组件
// import Button from '@/components/Button.vue'
// import Card from '@/components/Card.vue'
</script>
<template>
<div>
<Header />
<Card>
<Button>点击我</Button>
</Card>
</div>
</template>Element Plus 集成
typescript
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
Components({
resolvers: [ElementPlusResolver()]
})
]
})使用示例:
vue
<script setup lang="ts">
// 无需导入 Element Plus 组件
const handleClick = () => {
ElMessage.success('操作成功')
}
</script>
<template>
<div>
<el-button type="primary" @click="handleClick">
主要按钮
</el-button>
<el-input v-model="input" placeholder="请输入内容" />
<el-table :data="tableData">
<el-table-column prop="name" label="姓名" />
<el-table-column prop="age" label="年龄" />
</el-table>
</div>
</template>Ant Design Vue 集成
typescript
// vite.config.ts
import Components from 'unplugin-vue-components/vite'
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
Components({
resolvers: [
AntDesignVueResolver({
importStyle: false // 使用 CSS
})
]
})
]
})使用示例:
vue
<template>
<div>
<a-button type="primary">主要按钮</a-button>
<a-input v-model:value="input" placeholder="请输入" />
<a-table :columns="columns" :data-source="data" />
</div>
</template>Naive UI 集成
typescript
// vite.config.ts
import Components from 'unplugin-vue-components/vite'
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
Components({
resolvers: [NaiveUiResolver()]
})
]
})使用示例:
vue
<template>
<n-config-provider>
<n-button type="primary">主要按钮</n-button>
<n-input v-model:value="input" placeholder="请输入" />
<n-data-table :columns="columns" :data="data" />
</n-config-provider>
</template>自定义组件目录
typescript
// vite.config.ts
Components({
// 指定多个组件目录
dirs: [
'src/components',
'src/views/components',
'src/layouts'
],
// 深度搜索子目录
deep: true,
// 组件名称转换
directoryAsNamespace: true
})目录结构:
src/
components/
common/
Button.vue
form/
Input.vue使用示例:
vue
<template>
<div>
<!-- 使用目录作为命名空间 -->
<CommonButton />
<FormInput />
</div>
</template>全局组件配置
typescript
// vite.config.ts
Components({
dirs: ['src/components'],
// 全局组件模式
globalComponentsDeclaration: true,
// 包含的文件模式
include: [/\.vue$/, /\.vue\?vue/],
// 排除的文件模式
exclude: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/]
})组件别名
typescript
// vite.config.ts
Components({
dirs: ['src/components'],
resolvers: [
// 自定义解析器
(componentName) => {
// 为组件添加前缀
if (componentName.startsWith('My'))
return { name: componentName.slice(2), from: '@/components' }
}
]
})完整配置示例
点击查看完整配置
typescript
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
// 自动导入 API
AutoImport({
imports: [
'vue',
'vue-router',
'pinia',
'@vueuse/core'
],
resolvers: [ElementPlusResolver()],
dts: 'src/auto-imports.d.ts',
dirs: [
'./src/composables',
'./src/utils'
],
eslintrc: {
enabled: true,
filepath: './.eslintrc-auto-import.json',
globalsPropValue: true
}
}),
// 自动导入组件
Components({
dirs: ['src/components'],
extensions: ['vue'],
deep: true,
resolvers: [ElementPlusResolver()],
dts: 'src/components.d.ts',
directoryAsNamespace: true
})
]
})配合使用示例
完整项目示例
点击查看完整代码
vue
<script setup lang="ts">
// 无需任何导入语句
// ref, computed, watch 来自 unplugin-auto-import
// UserCard, ProductList 来自 unplugin-vue-components
// useMouse, useLocalStorage 来自 @vueuse/core
const count = ref(0)
const doubled = computed(() => count.value * 2)
const { x, y } = useMouse()
const savedCount = useLocalStorage('count', 0)
// 监听变化
watch(count, (newVal) => {
savedCount.value = newVal
})
const increment = () => {
count.value++
}
const reset = () => {
count.value = 0
}
// 用户数据
const users = ref([
{ id: 1, name: '张三', avatar: '/avatars/1.jpg' },
{ id: 2, name: '李四', avatar: '/avatars/2.jpg' }
])
</script>
<template>
<div class="p-6">
<!-- 使用自定义组件,无需导入 -->
<Header title="自动导入示例" />
<div class="grid grid-cols-2 gap-4 mb-6">
<Card>
<h3 class="text-lg font-bold mb-4">计数器</h3>
<p>当前值: {{ count }}</p>
<p>双倍值: {{ doubled }}</p>
<p>保存的值: {{ savedCount }}</p>
<div class="mt-4 space-x-2">
<Button @click="increment">增加</Button>
<Button @click="reset" type="danger">重置</Button>
</div>
</Card>
<Card>
<h3 class="text-lg font-bold mb-4">鼠标位置</h3>
<p>X: {{ x }}</p>
<p>Y: {{ y }}</p>
</Card>
</div>
<!-- 使用 Element Plus 组件,无需导入 -->
<el-card class="mb-6">
<template #header>
<span>用户列表</span>
</template>
<div class="space-y-4">
<UserCard
v-for="user in users"
:key="user.id"
:user="user"
/>
</div>
</el-card>
<!-- 使用产品列表组件 -->
<ProductList />
</div>
</template>TypeScript 支持
类型声明文件
配置后会自动生成类型声明文件:
typescript
// src/auto-imports.d.ts
// 自动生成,不要手动修改
declare global {
const ref: typeof import('vue')['ref']
const computed: typeof import('vue')['computed']
const watch: typeof import('vue')['watch']
// ... 更多
}
export {}typescript
// src/components.d.ts
// 自动生成,不要手动修改
declare module 'vue' {
export interface GlobalComponents {
Button: typeof import('./components/Button.vue')['default']
Card: typeof import('./components/Card.vue')['default']
// ... 更多
}
}
export {}tsconfig.json 配置
json
{
"compilerOptions": {
"types": ["vite/client"]
},
"include": [
"src/**/*",
"src/auto-imports.d.ts",
"src/components.d.ts"
]
}ESLint 配置
解决 no-undef 错误
json
// .eslintrc.json
{
"extends": [
"./.eslintrc-auto-import.json"
]
}或手动配置:
json
{
"globals": {
"ref": "readonly",
"computed": "readonly",
"watch": "readonly"
}
}最佳实践
1. 合理使用自动导入
vue
<script setup lang="ts">
// ✅ 推荐:常用的 Vue API 自动导入
const count = ref(0)
const doubled = computed(() => count.value * 2)
// ✅ 推荐:业务组件自动导入
// <UserCard /> 自动导入
// ⚠️ 注意:复杂类型还是建议手动导入
import type { User, Product } from '@/types'
</script>2. 组织组件目录
src/
components/
common/ # 通用组件
Button.vue
Card.vue
business/ # 业务组件
UserCard.vue
ProductList.vue
layout/ # 布局组件
Header.vue
Footer.vue3. 性能优化
typescript
// vite.config.ts
Components({
// 只在开发环境生成类型声明
dts: process.env.NODE_ENV === 'development' ? 'src/components.d.ts' : false,
// 排除不需要的目录
exclude: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/, /[\\/]dist[\\/]/]
})4. 团队协作
typescript
// 统一配置文件
// config/auto-import.config.ts
export const autoImportConfig = {
imports: [
'vue',
'vue-router',
'pinia',
'@vueuse/core'
],
dirs: [
'./src/composables',
'./src/utils'
]
}
// vite.config.ts
import { autoImportConfig } from './config/auto-import.config'
export default defineConfig({
plugins: [
AutoImport(autoImportConfig)
]
})注意事项
1. 类型声明文件
- ⚠️ 生成的
.d.ts文件应该提交到版本控制 - ⚠️ 不要手动修改自动生成的类型文件
- ⚠️ 确保
tsconfig.json包含这些文件
2. ESLint 配置
- ⚠️ 需要配置 ESLint 全局变量,避免
no-undef错误 - ⚠️ 生成的
.eslintrc-auto-import.json应该提交到版本控制
3. IDE 支持
- ⚠️ VSCode 需要安装 Volar 插件
- ⚠️ 重启 IDE 以获取最新的类型提示
- ⚠️ 如果类型提示不生效,尝试重新生成类型文件
4. 性能考虑
- ⚠️ 不要导入过多不必要的库
- ⚠️ 合理设置
dirs避免扫描过多文件 - ⚠️ 使用
exclude排除不需要的目录
5. 命名冲突
- ⚠️ 注意组件名称不要与 HTML 标签冲突
- ⚠️ 避免自定义函数与自动导入的函数同名
- ⚠️ 使用命名空间避免冲突
常见问题
1. 类型提示不生效
bash
# 删除类型文件重新生成
rm src/auto-imports.d.ts src/components.d.ts
# 重启开发服务器
pnpm dev2. ESLint 报错 no-undef
json
// .eslintrc.json
{
"extends": ["./.eslintrc-auto-import.json"]
}3. 组件未自动导入
检查配置:
typescript
Components({
dirs: ['src/components'], // 确保路径正确
extensions: ['vue'], // 确保扩展名匹配
deep: true // 启用深度搜索
})4. 打包体积过大
typescript
// 使用按需导入
Components({
resolvers: [
ElementPlusResolver({
importStyle: 'sass' // 或 false
})
]
})