开端
最近一通疯狂的忙碌,终于将多端api封装完成了,马上就可以跟同事们就行吹水了。然而,意外还是发生了,就在我进行最后一轮的测试的时候,忽然发现包的体积好像是有一丢丢大(其实,对于小程序来说是体积是很大了)。
单个分包/主包大小不能超过 2M
相信有的小伙伴肯定经历过解决超包的问题,那真的是酸爽之极。既然超包问题如此难以解决,那么我们对于加入主包的每一KB都需要慎之又慎,更何况增加如此大的体积。
下面我们就先来看一下问题发生的原因吧。
问题分析
幸运的是,我们采用的是Taro框架,打包的底层实现是基于webpack实现的。这里我们先借助于 webpack-bundle-analyzer
来查看一下现在的依赖包的大小。
Taro中使用 webpack-bundle-analyzer 的配置代码如下:
js
webpackChain (chain) {
if (process.env.NODE_ENV === 'production') {
chain
.plugin('analyzer')
.use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, []);
}
}
构建完成之后,这个时候我们就看到了当前包的体积情况了。
通过一番查找,我们对现在的包体积情况得出结论是这样的:
包的总体积:286.41K
方法库的体积:28.8K
仔细看了一下方法库的内容,发现是把方法库整个给打包进来了,没有删除我们没有使用到的方法,也就是我们常说的没有实现按需加载。
为什么会这样呢?
反复查询了webpack官方对 Tree shaking 的描述,最终发现了这样一段有玄机的内容:
tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)
新的 webpack 4 正式版本,扩展了这个检测能力,...,可以安全地删除文件中未使用的部分
原来是这样,我们再回过来梳理一下。
在方法库中,我们通过 export default xxx
将方法库导出了,然后在项目中通过 xxx.xxx 对方法进行了使用。而在方法库中,我们又将所有的方法模块挂载到了 xxx 上。在这一通操作之下,我们就是对代码块进行了使用,这样就无法满足Tree shaking的情况了。
现在问题定位到了,没有按需加载。但是,方法库又该如何实现按需加载呢?
解决方案
我的脑海第一时间跳出的就是 loadsh
(我曾经是lodash的重度使用者)。lodash可以进行按需加载,作为同样的方法库,我是否可以借鉴lodash的思路来实现呢?
鉴于网上有很多有关lodash的按需加载的深入详细的说明了,我就不再班门弄斧了。
通过对lodash的按需加载方式的深入研究,我发现了其中的一个核心点:使用lodash-es依赖替换了lodash依赖 ,而 babel-plugin-lodash
和 lodash-webpack-plugin
插件更多的是为webpack的Tree shaking服务,将loadsh的引用改造成可shaking的方式。
ok,按照思路,我们先实现第一步,将方法库打包成es模块的方式。
ES模块化编译
我们开干之前,先来看一下lodash-es的模块情况。
是不是很意外?是不是很惊喜?
通过lodash的源代码我们可以看出来,所谓的es模块不是我们所理解的单纯的将代码以 ES Module 的方式导出,而是将每个方法以 ES Module 的方式导出为一个单独的文件模块。
我们的方法库采用的是rollup.js的方式进行打包的,这里我们将之前的构建方式改造一下:
css
export default {
input: {
index: 'src/main-a.js',
canIUse: 'src/api/base/canIUse',
...
},
output: {
dir: 'lib',
format: 'es',
exports: 'named',
sourcemap: true,
},
...
};
下面来看一下我们的构建结果,是不是已经跟lodash-es的一样了呢!
实现按需加载
虽然实现了方法库的ES Module,但是还有一个问题困扰着我们。
大家进行方法库的使用的时候,习惯的是使用 import {xxx} from 'api'
,而不会大量的进行 import { xxx } from 'api/lib/xxx'
。
这可如何是好呢?
忽然我脑海灵光一现,我想到了antd进行按需加载的时候配置过的 babel-plugin-import
。对啊,我只需要借助于 babel-plugin-import 在编译的时候,将api方法映射到对应的lib文件夹下的方法模块就ok了呀。
具体配置如下:
arduino
[
'import',
{
"libraryName": "xxx",
"libraryDirectory": "lib",
"camel2DashComponentName": false,
},
'xxx',
],
成果
配置完成了,我们来验证一下吧。
通过查看构建结果我们可以发现,成果大大的有:
包的总体积:259.18K
方法库的体积:1.58K
通过比较,我们初步得出结论,体积我们减少了 27.23k ,主包体积减少了 27.22k。对于小程序来说,这个空间是相当可观的。当然,如果我们在主包中使用更多的方法的话,体积会略有增大,但是肯定远远到不了优化之前的体积。
总结
我们来总结一下如何对方法库进行Tree shaking:
-
将方法库以 ES Module 的模式进行打包输出
-
配置 babel-plugin-import 将方法的引入路径映射到lib下