浅析组件库实现按需引入的两种方法

发布于 5 天前  10 次阅读


组件库会包含几十甚至上百个组件,但是应用的时候往往只使用其中的一部分。这个时候如果全部引入到项目中,就会使输出产物体积变大。按需加载的支持是组件库中必须考虑的问题。

目前组件的按需引入会分成两个方法:

  • 经典方法:组件单独分包 + 按需导入 + babel-plugin-component(自动化按需引入);
  • 次时代方法:ESModule + Treeshaking + 自动按需importunplugin-vue-components 自动化配置)。

经典方法

目前多数主流组件库实现按需引入的方式使用的是经典方法,而使用经典方法的基础是,组件单独分包。

最简单的按需引入

因为每个组件都是一个单独的包,我们可以只引入某个组件,比如:

import Button from 'xxx-ui/packages/button';
import 'xxx-ui/theme-chalk/button.css';

Vue.use(Button);

这样我们就只引入了Button相关的文件,而不会包含其他组件。这样的问题是比较麻烦,使用成本较高,使用者需要知道组件库的一些路径等。最理想的方式还是下面这种:

import { Button } from 'xxx-ui';

通过 babel 插件实现转换

既然想通过以上这种简单的方式引入组件和相关的样式文件等文件,那么我们只要帮使用者把第一种方式转换成第二种就可以了。而通过babel插件来转换对用户来说是无感的。主流组件库也是这样实现的,比如Element UI使用的是babel-plugin-component插件。大致思路就是这样,如果需要实现这种方式的按需引入,则需要为自己的组件库开发一个类似的babel插件,通过AST抽象语法树进行一个转换。具体实现可参考babel-plugin-import源码。

Ant Design也是采用这种方式,只是使用的插件不一样。它用的是babel-plugin-import,其实babel-plugin-component就是forkbabel-plugin-import的。

次时代方法

除了组件独立分包后通过babel插件实现按需引入的转换这种经典方法,还有种更简单的方法,即利用Tree-shaking

组件库应打包为 es module 模块

commonjs规范是最常见的使用方式,umd一般用于CDN方式直接在页面引入,而es module就是用来实现Tree Shaking的,为什么es module能实现Tree Shakingcommonjs规范不行呢,原因是es module是静态编译的,也就是在编译阶段就能确定某个模块导出了什么,引入了什么,代码执行阶段不会改变,所以打包工具在打包的时候就能分析出哪个方法被使用了,哪些没有,没有用到的就可以放心的删掉了。

此外,需要在组件库的package.json文件中的"module"属性指定包的es module模块入口,会优先于"main"属性生效。

组件库应按规范导出各组件

接下来我们还要修改一下组件库中各组件的导出方法,不独立分包,所有组件暴露在一个入口文件中。这样才能支持以下这种使用方式:

import { Button } from 'xxx-ui';

我们需要保证最终在入口文件(如index.js)中导出各具名组件,如:

// 可以在入口文件引入全局样式文件,同时配置 sideEffects 以保证 tree-shaking 不会把样式文件移除
// import './style.css';
// 如果没有配置 sideEffects 在主项目中使用组件库时还需要手动引入打包产物中的样式文件
// ...

import XButton from '@/components/button';
import XInput from '@/components/input';
import XDialog from '@/components/dialog';
// ...

export {
  XButton,
  XInput,
  XDialog,
  // ...
}

// ...

按需配置 sideEffects

我们在入口文件中引入了全局样式文件style.css,但是并没有明显的进行使用,这时,tree-shaking就会把这种没有明显进行使用的模块移除。但我们显然是不想让样式文件被移除的,这样在主项目中就不需要再手动显式引入样式文件了。

那么我们就需要告诉主项目的打包工具哪些文件是没有副作用的,可以删掉,哪些是有的,需要保留。如果主项目的打包工具是webpack,那么我们就需要在package.json文件中新增一个sideEffects属性:

{
  // ...
  "sideEffects": ["**/*.css"],
  // ...
}

这样就大功告成了。

使用 unplugin-vue-components 插件实现自动引入

有些主流组件库还支持自动按需引入你使用的组件,使用者可以在模板里直接使用,由插件来扫描引入并注册。当然,这个插件还需要我们自己实现。具体实现可参考unplugin-vue-components插件的实现或其他开源插件的实现。

参考资料


A Student on the way to full stack of Web3.