原来Vue3中组件可以不引入、不注册就能使用!

2,104 阅读5分钟

问题简介

前几天看到同事的一段代码,我看到他使用了一个名为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内容即可: