很多的团队都会根据自己团队的技术风格或应用场景建立自己的组件库,然鹅随着组件的增加,组件库的体积也变得越来越大,如果一个很小的项目,仅仅想使用某个组件库中一两个组件,而这个组件库十分庞大却不支持按需加载,其结果就是打包后发现引入组件库的体积甚至比项目本身的代码还要大N倍。
那么如何让组件库支持按需加载呢?
以React项目为例,首先从打包后的包来分析,可能在没有支持按需加载时类似这样:
.
├── dist
| ├── index.js
| ├── style.css
| ├── ...
| └── package.json
或者干脆直接不拆分出css
,像这样:
.
├── dist
| ├── index.js
| ├── ...
| └── package.json
此时在项目中使用时,即便只使用组件库中的一个组件,也要从 dist
中 import
index.js
这个包含所有组件的模块。
那么按需加载想要的效果呢?我们希望使用哪个组件,便导入哪个组件相关的模块,从而减少不必要的代码导入。由此想到:不能将所有的组件都打包到一个模块(js文件),要将每个组件单独打包到不同的模块,然后按需独立引入。同时还要支持全量使用。于是我们想要的打包结果可能是:
.
├── dist
| ├── componentA.js
| ├── componentB.js
| ├── index.js // 全量
| ├── ...
| └── package.json
此时就需要从webpack
的entry
配置入手了。
weboack entry说明文档。
首先我们的项目目录是这样的:
.
├── components
| ├── componentA
| | ├── style.scss
| | └── index.js
| ├── componentB
| | ├── style.scss
| | └── index.js
| ├── ...
| └── index.js
├── webpack.config.js
└── ...
组件代码:
/components/componentA/index.js
export default function ComponentA() {
return (
...
)
}
/components/index.js
export { default as ComponentA } from './componentA';
export { default as ComponentB } from './componentB';
...
想要实现分多个模块打包,则需通过entry
指定多个入口,像这样:
{
entry: {
index: path.resolve(__dirname, './components/index.js'),
componentA: path.resolve(__dirname, './components/componentA'),
componentA: path.resolve(__dirname, './components/componentA'),
....
},
output: {
path: path.resolve(__dirname, './dist'),
filename: '[name].js'
}
}
此时打包后便可得到我们想要的效果,有个麻烦是,我们每加一个组件都要在entry
这个加一个入口,这就很难受了,因此可以通过node
来动态获取入口,比如:
const path = require('path');
const fs = require('fs');
const getEntry = () => {
let entryObj = {};
let dir = fs.readdirSync(resolve('./components/'));
dir.forEach(item => {
item === 'index.js'
? entryObj['index'] = resolve(`./components/index.js`)
: entryObj[item] = resolve(`./components/${item}`)
});
return entryObj
}
// entry处配置
{
entry: getEntry()
}
这样方便又准确!
这是我们在使用时便可,直接引入单个组件了:
import ComponentA from 'xxx/componentA';
这时你可能会发现,当我需要使用多个组件时便要多次import
,写起来很麻烦:
import ComponentA from 'xxx/componentA';
import ComponentB from 'xxx/componentB';
...
如果css
文件单独抽离出来还要多import
个css
。
而我们更倾向于可以像这样来使用:
import { ComponentA, ComponentB } from 'xxx';
当然是可以的,不过需要借助于一个工具————babel
。要做的就是做一个简单的babel插件
,而插件的功能就是:
将
import { ComponentA, ComponentB } from 'xxx';
转化为=》
import ComponentA from 'xxx/componentA';
import ComponentB from 'xxx/componentB';
Babel
的转码工作大致分为三个阶段:
- 解析(
parse
):将代码字符串解析成AST
(抽象语法树) - 转换(
transform
):对抽象语法树进行转换操作 - 生成(
generate
): 将变换后的抽象语法树再生成代码字符串
具体逻辑不再叙述,可参考babel插件手册自己开发。
看完略有收获,望不吝点赞,如有差错,劳烦您不吝赐教~