1. 需求背景
开发立新二手车小程序(以下简称门店小程序),车辆管理需求的过程中发现,门店小程序的包体积大小过大。其中生成环境1380kb,开发环境2839kb。
1.1 主包大小对生产环境
之前我们为了优化包的体积大小,做了一次本地图片上cdn。但显然生产环境中的主包大小,已经接近小程序对于主包大小2M的限制条件,不利于我们后面新业务的开发上新。而且主包的提交大小也影响了用户打开小程序的速度,和小程序运行时所占用的系统内存。一般来说小程序的主包控制在1M,可以让用户在1s内打开小程序。
1.2 小程序的启动步骤
包的大小将直接影响,第一步资源准备和第二步业务代码注入和渲染的速度。
1.3 主包大小对生产环境
开发环境中的主包大小已经超过了限制条件,开发过程中无法进行小程序预览调试,真机中的预览调试,每次都需要进行一次压缩打包,影响了开发效率。
2. 分析mpvue打包后的主包大小组成部分
因为开发环境中的打包结果相较于生产环境中,只多不少。所以以下分析我们全部以开发环境下来分析,
mpvue将每个注册的page作为webpack打包的入口,同时也会将系统中引入的公共库或者引入次数较多的文件,打包到一个公有代码文件中。其中在我们的项目中使用到了一个自有的小程序组件库,在webpack的打包构成中也会引入进来。
2.1 mpvue打包后的包结构
这里除了pages业务分包文件夹,其他的文件都是主包的内容。但是不用担心,这里我们只看common、static两个文件夹就可以了。因为这两个文件夹一个公共库提取的部分,一个自有小程序组件的部分。占到了主包体积的84%
2.2 common中的vendor.js
vendor.js是common的主体部分,是通过webpack的CommonsChunkPlugin插件提取出来的公有库文件。
我们来看webpack的包分析插件BundleAnalyzerPlugin给出的分析结果。
右边的src是业务逻辑里面使用到的公有库代码不是我们的优化的目标,我们看到node_modules一共是1.51M的体积,是我们重点要处理的,但是这里bundle-analyzer给出的分析是很不详细而且是不准确的,比如说core-js(babel的语法垫片)体积只有71kb在这里确是图示的一半。
2.3 CommonsChunkPlugin
CommonsChunkPlugin是webpack在打包时用力提取公共代码的插件。
我决定在CommonsChunkPlugin里做一些修改,既然vendor.js是通过CommonsChunkPlugin打包出来的,我们就可以让它打印出更加详细一些的信息。
我们在代码里简单的做个分类,node_modules中的引入文件我们直接打印出来,而在src中分出VuexStore,和MockjsConfig这两个零碎但是占比大的文件类型来进行累计。最后把它们按照体积来简单的排个序,这里我们偷个懒,直接定位到最后打包的文件时输出信息。当然你也可以写一个webpack插件,在打包结束时来打印这些信息。
这里我们简单的修改以下CommonsChunkPlugin组件中的配置,添加了一个环境变量来控制组件是否执行。以下是这个函数的输出结果。
这次我们拿到了更加细致的打包信息,上马就有了优化的思路。
- 我们看到lodash有夸张的325kb之多,但是我们在项目里只是用到了一个函数,这是不应该的。
- echarts也有142kb的体积,但是我们只有在一个页面用到了echarts,所以我们应该把它分离出主包。
- mockjs是我们本地的开发环境下使用的模拟数据工具,和其数据配置一起算有140kb之多,为了减少我们开发环境下的主包大小。这里我们可以把它移出本地。
3. lodash的引入
查看项目代码,发现在项目中lodash的引入方式是这样
这样的引入方式是会将整个lodash的包都引入到js中的。而lodash提供了我们引入单个的fn的方法。
这样的话我们就可以只引入lodash中的debounce函数所包含的js包了。我们将项目中所有的lodash引入方式做一次修改,跑一次我们的分析函数。
现在lodash打入包的体积只有61kb了。
3.1 lodash在小程序中的兼容问题。
值得一提的是,lodash有一些方法使用到了js全局的函数像是Math。但是在小程序中对于全局对象的识别可能会有误。
上面这段函数识别到的对象是小程序中的global对象,感兴趣的同事可以,copy代码到小程序开发环境中执行以下。然而小程序中的global对象上是不会挂载像Math、Regex、Symbol等这样的全局对象和方法的。所以我们需要在项目的入口文件中加入一段兼容性的代码。如下所示。
事实上lodash这段对于宿主对象的识别方式,是被广泛应用的。所以在使用其他库的时候我们也需要注意这个问题。
4. echarts 的打包
要将echarts打入到别的子包也是很容易的,我们只需要简单修改一下CommonsChunkPlugin的配置就可以了。
这里我们将判断是否需要打入公共包的判断条件改一下,在node_modules的判断中加上一个限制:count > 1,这样只有被引入过两次的node_modules包才会被打入到公共的js包中。这样echarts就只会被打包到使用到它的分包中了。
— —
然而echart只是在车辆估计参考中,一个线性的统计图里用到了。我觉得echarts在这里有点大才小用了,其实我们完全可以自己用canvas,来实现这里的线性统计图的需求。而现在打开查看参考估价,我们可以感受到明显的等待时间。所以这里是可以进一步优化的。
### 5 mockjs脱离出开发环境下的主包
这里我的处理方式是,新建一个node.js的服务来提供mockjs的模拟数据,代替mockjs在本地拦截http请求制造mock数据的方式。
#### 5.1 node端的代码实现
在mockServer中主要有两个逻辑,一个是获取在mockServer中配置的api列表,前端拿到这个列表会在ajax中对列表中的api加上 /mock_req 的前缀。为系统的网络代理提供转发的标识。
5.2 前端的代码实现
5.3 MacOs环境下代理工具charls的配置
配置如下
5.4 使用时候的注意事项
- 微信开发者工具: 设置-代理设置-使用系统代理
- charls代理已开启
- 小程序在开发模式下,点击标题栏的mock按钮开启mock,如有新增匹配接口,点击同步mock配置。
6 自有组件库wxapp-lui的地址组件大小优化
在我们的项目中使用到了一个自有的小程序组件库wxapp-lui,在webpack的打包构成中也会引入进来,在项目中则是打入到static目录中,和项目中使用的静态图片一起。其中打包进地址数据的address组件大小有324kb,有很大的优化空间。再者因为wxapp-lui项目编译并不支持npm包的引入,所以address组件引入的地址数据是单独以文件的方式引入的,和公司其他项目中使用city-data的npm包管理地址数据的方式不同。这次一并做了修改。
6.1 npm包引入的支持
这里先是按照小程序上对npm支持的方式做了一次npm引入,发现并不理想,管理起来也不方便,这个方案被废弃掉了。这里具体的原因不做赘述。最后我决定用rollup直接把npm包中的源码打入到小程序组件的源码中。配置的代码如下。
更多细节和原理可以查看rollup的文档。
6.2 address组件地址数据格式的修改
address的地址数据,和我们之前在企业号移动端组件库,cvux中address组件使用的格式是一样的。这套格式存在很多冗余的数据。
我们可以看到"parent": "110100"
这一条就出现了很多次。我发现wxapp-lui中的address组件,在逻辑上对与格式并不是很依赖。直接把它的数据源改成后台的源数据。
显然上面的结构简洁了很多。再稍微修改一下address内部的逻辑代码就完成了。编译以后大小降到了100kb,如果觉得还是挺大的,后面我们可以直接把这个数据放到cdn上。压缩以后也只有74kb的大小了。
总结
经过这一系列的修改后,我们的门店小程序在开发环境下的主包大小已经降到了1580kb,正式环境下736kb。减少了将近一半体积的主包大小。
一个小提示
微信开发工具上面介绍的性能面板,使用开发版本打开。抓包发现扫码打开的开发版本会下载全部的代码,包括分包和主包。所以在这个场景下,上面关于小程序打开时的性能参数就没有了参考价值。