问题简介
前几天看到同事的一段代码,我看到他使用了一个名为PhotoCard的组件,但是并没有在项目中引入。
我以为他把PhotoCard组件全局注册了,于是在代码中找全局注册的逻辑,但是始终没有找到。
我有些错乱,我记得Vue3中可以不注册组件就使用,难道现在连引入都不用了吗?
于是,我翻阅了一下官方文档:
我发现即使使用setup语法糖,也是需要显示引入组件的。最后,我还是不明白为啥同事的PhotoCard的组件不引入就可以使用!
于是,我请教了同事,才知道他使用了unplugin-vue-components插件实现了组件的自动引入与按需加载!
牛啊!
组件的自动引入unplugin-vue-components
我们先看看unplugin-vue-components这个插件的官方介绍:
好家伙,这个插件确实很厉害,使用它我们的项目可以做到不需要手动引入组件,也不需要手动注册组件。而且,它还是按需加载的!
于是,我看了下项目中的配置,果然发现了相关的配置,但是项目中引入的是另一个插件
vite-plugin-components
一番搜所后才发现vite-plugin-components
是早期的 Vite 插件 ,现在已经停止维护了。随着 Vue 生态的发展,unplugin-vue-components
逐渐成为更先进、更广泛使用的解决方案。因此,官方现在建议开发者迁移到 unplugin-vue-components
了。
如何使用
unplugin-vue-components的使用非常简单,项目中先需要安装
npm i unplugin-vue-components -D
然后,参考官方案例,我们很容易就可以配置它。
Vite项目
// vite.config.ts
import Components from 'unplugin-vue-components/vite'
export default defineConfig({
plugins: [
Components({}),
],
})
Vue CLI或webpack项目
// vue.config.js
module.exports = {
/* ... */
plugins: [
require('unplugin-vue-components/webpack').default({}),
],
}
// webpack.config.js
module.exports = {
/* ... */
plugins: [
require('unplugin-vue-components/webpack').default({}),
],
}
配置好后,我们的vue项目中不引入组件就可以使用了,像这样:
原理简析
unplugin-vue-components的大致原理如下:
扫描文件系统:根据配置的 dirs
(目录)、extensions
(文件扩展名)、globs
(文件匹配模式)等选项,插件会扫描项目中的组件文件。
生成组件映射:
- 扫描的组件文件会被解析并生成一个 **组件名称到文件路径的映射表**。
- 例如,`MyComponent.vue` 会映射为 `{ MyComponent: '/path/to/MyComponent.vue' }`。
动态注册:
- 插件会在构建过程中,动态将这些组件全局注册为 Vue 组件。
- 在 Vue 模板中使用组件时,如果匹配到映射表中的名称,Vue 会自动引入对应的组件文件。
按需加载:如果项目中使用了支持按需加载的第三方组件库,可以通过配置 resolvers
,动态解析组件名称,并自动引入对应的库文件。
类型支持(可选) :如果启用了 TypeScript,插件会生成一个 components.d.ts
文件,提供组件的全局类型声明,使开发过程更友好。
可选项配置
参考官方介绍,他的全量配置如下
{
// 搜索组件的相对路径目录。
dirs: ['src/components'],
// 组件的有效文件扩展名。
extensions: ['vue'],
// 匹配文件名以检测为组件的 Glob 模式。
// 如果指定此项,`dirs`、`extensions` 和 `directoryAsNamespace` 配置将被忽略。
// 使用前置 `!` 的负模式可以排除特定组件。
globs: ['src/components/*.{vue}'],
// 是否递归搜索子目录。
deep: true,
// 自定义组件解析器的数组。
resolvers: [],
// 是否生成 `components.d.ts` 全局声明文件,
// 也可以设置为自定义文件名路径。
// 默认值:如果安装了 TypeScript,则为 `true`。
dts: false,
// 是否将子目录名称作为组件的命名空间前缀。
directoryAsNamespace: false,
// 合并目录和组件中相同的前缀(区分大小写),
// 以避免命名冲突。
// 仅在 `directoryAsNamespace: true` 时生效。
collapseSamePrefixes: false,
// 忽略命名空间前缀的子目录路径列表。
// 仅在 `directoryAsNamespace: true` 时生效。
globalNamespaces: [],
// 是否自动导入指令。
// 默认值:Vue 3 为 `true`,Vue 2 为 `false`。
// 如果在 Vue 2 中启用,需要安装 Babel 转换器:
// `npm install -D @babel/parser`
directives: true,
// 在解析路径之前,对路径进行转换的函数。
importPathTransform: v => v,
// 是否允许组件覆盖其他同名组件。
allowOverrides: false,
// 过滤器,用于转换目标文件(即需要插入自动导入的组件)。
// 注意:此选项并非用于控制组件的注册。
include: [/.vue$/, /.vue?vue/],
// 用于排除不需要扫描的文件或目录的过滤器。
exclude: [/[\/]node_modules[\/]/, /[\/].git[\/]/, /[\/].nuxt[\/]/],
// 用于过滤不需要自动导入的组件名称。
// 例如用于异步组件或其他冲突的全局组件。
excludeNames: [/^Async.+/],
// 项目使用的 Vue 版本。
// 如果未指定,将自动检测。
// 可接受的值:`2` | `2.7` | `3`。
version: 2.7,
// 仅为库中全局注册的组件提供类型声明。
types: []
}
上面的配置比较多,我们可以看一下它的默认配置(去除了一些不常用的)
Components({
// 搜索组件的默认目录
dirs: ['src/components'],
// 默认文件扩展名
extensions: ['vue'],
// 是否递归搜索子目录
deep: true,
// 默认不配置自定义解析器
resolvers: [],
// 自动生成 `components.d.ts`,如果安装了 TypeScript 则默认为 `true`
dts: true, // 如果没有安装 TypeScript,默认值为 false
})
自动引入组件库
除了自动引入组件,经过一些配置后,他还能实现自动引入组件库的功能!非常强大!
比如我们要实现Element Plus的自动引入,只需要配置如下即可:
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({
dirs: ['src/components'], // 扫描本地组件 默认值,可不写
extensions: ['vue'], // 支持的文件扩展名 默认值,可不写
deep: true, // 是否递归扫描子目录 默认值,可不写
dts: 'src/types/components.d.ts', // 自动生成类型声明文件
resolvers: [
ElementPlusResolver(), // 支持 Element Plus 按需加载
],
}),
],
});
非常简单啊!
而且,我们还可以自定义引入组件逻辑!比如,我们公司套壳的UI组件名称叫super-vant,我想实现它的自动引入,只需要自定义resolvers内容即可: