{T}

组件自动导入与 UI 库集成实践

概述

unplugin-vue-components 实现组件的按需自动导入,消除手动 import 的重复工作。本文讲解其配置方式、命名空间策略,以及与 Element Plus、Naive UI 等 UI 库的 Resolver 集成。

学习目标

  • 掌握 unplugin-vue-components 的安装与目录扫描配置
  • 理解组件命名空间与目录映射策略
  • 学会通过 Resolver 集成第三方 UI 库的按需导入

一、DRY 原则与自动导入

1.1 传统方式 vs 自动导入

Vue SFC
<!-- 传统:手动导入 -->
<script setup>
import UserCard from '@/components/UserCard.vue'
import AppHeader from '@/components/AppHeader.vue'
</script>

<!-- 自动导入:直接使用 -->
<template>
  <AppHeader />
  <UserCard />
</template>

1.2 核心价值

  • 消除重复的 import 语句
  • 组件名即文件名,降低心智负担
  • 配合 TypeScript 生成完整类型提示
  • 支持 Tree-shaking,未使用组件不打包

二、安装与配置

2.1 安装

bash
pnpm add -D unplugin-vue-components

2.2 Vite 配置

typescript
// vite.config.ts
import Components from 'unplugin-vue-components/vite'

export default defineConfig({
  plugins: [
    vue(),
    Components({
      dirs: ['src/components'],
      extensions: ['vue'],
      dts: 'components.d.ts',
      directoryAsNamespace: false,
      collapseSamePrefixes: false,
    }),
  ],
})

2.3 配置项说明

配置项说明默认值
dirs扫描目录['src/components']
extensions文件扩展名['vue']
dts类型声明输出路径'components.d.ts'
directoryAsNamespace目录名作为组件前缀false
collapseSamePrefixes折叠重复前缀false
resolversUI 库解析器[]

三、命名空间策略

3.1 无命名空间(默认)

code
src/components/
├── UserCard.vue      →  <UserCard />
├── AppHeader.vue     →  <AppHeader />
└── base/
    └── Button.vue    →  <Button />

3.2 目录作为命名空间

typescript
Components({
  directoryAsNamespace: true,
})
code
src/components/
├── UserCard.vue      →  <UserCard />
└── base/
    └── Button.vue    →  <BaseButton />

避免不同目录下同名组件冲突。


四、UI 库 Resolver 集成

4.1 Element Plus

bash
pnpm add element-plus
typescript
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

Components({
  resolvers: [ElementPlusResolver()],
})

4.2 Naive UI

typescript
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'

Components({
  resolvers: [NaiveUiResolver()],
})

4.3 自定义 Resolver

typescript
Components({
  resolvers: [
    (componentName) => {
      if (componentName.startsWith('My')) {
        return {
          name: componentName,
          from: `@/my-lib/${componentName}`,
        }
      }
    },
  ],
})

五、类型声明

5.1 components.d.ts

插件自动生成,为所有可自动导入的组件提供类型:

typescript
// components.d.ts(自动生成)
declare module 'vue' {
  export interface GlobalComponents {
    UserCard: typeof import('./src/components/UserCard.vue')['default']
    AppHeader: typeof import('./src/components/AppHeader.vue')['default']
    ElButton: typeof import('element-plus/es')['ElButton']
  }
}

5.2 tsconfig 引入

json
{
  "include": ["components.d.ts"]
}

常见问题

Q: 自动导入的组件在单元测试中如何识别?

在测试配置(如 vitest.config.ts)中也引入 Components 插件,或在测试文件中手动 import 被测组件。

Q: 如何排除某些组件不自动注册?

使用 exclude 配置项,或将组件放在扫描目录之外。通常 layouts/composables/ 目录不应被扫描。

Q: 组件名冲突如何处理?

启用 directoryAsNamespace: true,用目录名作为前缀区分。或调整 dirs 扫描顺序,先扫描的目录优先级更高。


延伸阅读