我们开发 Vue 项目时,一般会使用组件库进行开发,组件库有两种加载方式:全局引入和按需引入
全局引入组件库,使用起来就非常方便,但是坏处就是产物体积大,对性能要求较高的项目不友好。
按需引入,可以减少体积大小,但需要通过以下方式加载组件:
import Button from 'ant-design-vue/lib/button'; import 'ant-design-vue/lib/button/style';
// 或者 ant-design-vue/lib/button/style/css 加载 css 文件
引入组件还需要引入样式,非常麻烦,因此有了 babel-plugin-import
来进行按需加载,加入这个插件后,可以省去 style 的引入。
import { Button } from 'ant-design-vue';
但这种仍然需要手动引入组件,而且还必须使用 babel
而 unplugin-vue-components
可以不需要手动引入组件,能够让开发者就像全局组件那样进行开发,但实际上又是按需引入,且不限制打包工具,不需要使用 babel
使用效果
以 Antd Vue
和 vite
为例:
import AutoImport from 'unplugin-auto-import/vite'; import Components from 'unplugin-vue-components/vite'; import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers' export default { plugins: [ Components({ resolvers: [ AntDesignVueResolver() ], }), ], };
这样就能自动引入 Antd Vue
的组件,不需要手动 import 组件以及组件样式,使用起来就像全局组件一样,但这是按需自动引入,可以减少产物大小。
<template> <a-button>按钮</a-button> </template>
这样直接使用即可
解析器
unplugin-vue-components
为主流的 UI 组件库提供了内置的支持,通过使用对应 UI 组件库的解析器(resolvers),就能自动引入对应的组件库组件及样式。
解析器可以是一个函数或者是对象
以对象为例:
{ type: 'component', resolve: (name: string) => { const map = { 'Abutton': 'Button' } const path = `ant-design-vue/es` const name = map[name] return { // Button name, // ant-design-vue/es from: path, // ant-design-vue/es/button/style/css sideEffects: `ant-design-vue/es/${name.toLowerCase()}/style/css`, } } }, } }
resolve
是一个函数,当遇到 a-button
时,就会调用该函数,入参是 AButton
函数需要返回一个对象,例如:
{ name: 'Button', from: 'ant-design-vue/es', sideEffects: 'ant-design-vue/es/button/style/css', }
这样 unplugin-vue-components
就会加入以下代码:
import { Button } from 'ant-design-vue/es' import 'ant-design-vue/es/button/style/css'
而 ant-design-vue/es/button/style/css
中,引入了 Button
组件相关的样式:
import '../../style/default.css'; import './index.css';
一般情况按需引入组件,需要把组件的样式也引入进来,这就是 sideEffects
的作用。
实现原理
可能大家会想到,将 Vue 处理成以下内容即可:
<template> <a-button>按钮</a-button> </template> <script setup> import { Button as AButton } from 'ant-design-vue/es' import 'ant-design-vue/es/button/style/css' </script>
看起来可行,但其实不行,因为你 script
有普通的写法和 setup 写法,如果要做,得兼容两种情况,比较复杂。
其实 unplugin-vue-components
的实现非常简单。我们直接来看看 Vue 编译后的代码:
可以看到,全局组件编译后会用 resolveComponent 包裹,该函数在 Vue 官网有说明,就是按名称找到已注册的全局组件。
unplugin-vue-components
只需要正则匹配 _resolveComponent
,拿到入参 a-button
,然后根据将名称标准化为 AButton
,然后交给解析器,解析器会返回如下对象:
{ name: 'Button', from: 'ant-design-vue/es', sideEffects: 'ant-design-vue/es/button/style/css', }
然后修改 Vue 编译后的代码(unplugin-vue-components
会在 Vue
插件之后执行)
引入以下代码:
import { Button as __unplugin_components_0 } from 'ant-design-vue/es'; import 'ant-design-vue/es/button/style/css'; const _sfc_main = {}
并且将 _resolveComponent
替换成 __unplugin_components_0
这就将 a-button
组件的全局使用,转换成了按需引入的方式
局限性
unplugin-auto-import
看起来非常厉害,但它也有些局限性:
- 不能作用于 JSX,因为插件只处理 template 模板的代码。不过JSX 则需要配合 unplugin-auto-import(用于自动引入变量)
- 在 Vite 中 CSS 无法 tree shaking,不过这个是 Vite 的问题。但 JS 仍然是可以 tree shaking,相对于全局注册组件,对构建产物体积的优化还是非常可观的
总结
使用 unplugin-vue-components
可以非常方便地实现按需引入组件的功能,从而减小项目体积、加快项目加载速度,提升用户体验。
它通过正则匹配 Vue 的全局组件(编译后使用 _resolveComponent
包裹),然后引入组件并替换 _resolveComponent
,因此这种方式,只适用于 template 模板编译后的代码,JSX 的自动引入,则需要配合 unplugin-auto-import(用于自动引入变量)
实际上, unplugin-vue-components
还能自动引入项目 components
目录下的组件,同时也支持自定义指定的自动按需引入,更多内容请查看 unplugin-vue-components
文档
如果这篇文章对您有所帮助,可以点赞加收藏👍,您的鼓励是我创作路上的最大的动力。也可以关注我的公众号订阅后续的文章:Candy 的修仙秘籍(点击可跳转)
另外,想要米哈游/腾讯内推的,也可以关注公众号找我内推。