前言
构建结果从64.41M
缩减为26.8M
,优化率58.4%
,同时构建时间也从10分钟
缩减为4分钟
,优化率也差不多60%
,这个数据只是针对我们这个项目,没有什么参考意义。但是通过这个例子学到uni-app
怎么分包,以及分包后出现问题对应的解决思路。注:vue2 + webpack
起因
业务端突然出现一个项目(uni-app开发)在k8s构建为h5的时候提示内存溢出,使用webpack-bundle-analyzer
插件对其构建结果分析,发现存在很多冗余代码,构建结果变大占用过多的内存。比如很多页面使用了echarts
图表,构建结果中就存在多份echarts
,其他的公共代码也是如此。这边先以echarts
作为示例,如下,我在两个页面pages/index/index
和pages/me/index
中使用了echarts
,这两个页面被打包为两个独立的chunk,并且都包含了echarts代码,而不是抽离为公共代码进行复用,所以造成了代码冗余。
在uni-app
打包为h5时,有多少个页面,就会打包为多少个chunk
,这是由于uni-app
对page.json
进行解析转换,构建了一份路由,路由中组件是通过懒加载的形式,chunk的名称为路由转换连接符生成的,大概如下这个样子,这个只是个简化版,后续会出一篇page.json
具体怎么转化为路由的文章。
{
path: '/pages/index/index',
component: () => import(/* webpackChunkName: 'pages-index-index' */ 'C:/uni-demo/src/pages/index/index.vue'),
},
从路由的结构可以看出,组件是异步加载的形式。我们再来看一下uni-app
的基础分包配置,这边chunks
的配置都是initial
,而initial
选项没办法抽离异步加载的公共模块,从而导致了公共的模块的重复引入到了构建结果中。
splitChunks: {
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
},
解决方式
分包实现
再来回顾一下uni-app
默认的splitChunks
,它chunks的值为initial
无法对于抽离异步加载的公共模块,所以我们的分包方式也很简单,让对应配置可以分离包含异步加载方式的公共模块就可以了,以下以uview-ui
为示例,priority
这个值是权重,值越大,权重越高,我们这边改为-9
优先于默认配置权重就可以,最主要的是把chunks
值设置为all
,这样子就可以抽离同步和异步的公共模块了。chunks文档
module.exports = {
configureWebpack: {
optimization: {
splitChunks: {
cacheGroups: {
'uview-ui': {
name: 'chunk-uview-ui',
test: /[\\/]node_modules[\\/]uview-ui[\\/]/,
priority: -9,
chunks: 'all',
},
},
},
},
},
};
页面示例
这个是示例项目路由,将index
页面放在主包,test
和passion
页面放在子包pages/child
"pages": [
{
"path": "pages/index/index",
}
],
"subPackages": [
{
"root": "pages/child",
"pages": [
{
"path": "test/index",
},
{
"path": "passion/index",
}
]
}
],
分包结果,输出对应文件
这个是打包后的结果,可以得出两个结论:
uview-ui
被分离出来了,说明我们配置的分包生效了- 每一个页面,都会打包出一个chunk,chunk的名称就是路由转换连接符生成的,印证了我上面所说的
uni-app
构造的路由结构
页面白屏啦
查看一下浏览器资源加载,发现chunk-uview-ui
文件没有被加载,打开index.html
也没看到chunk-uview-ui
这个js文件。这个js文件是首页渲染必须的,现在没有注入到index.html,导致这个js没有被加载,所以页面白屏了。我们来分析一下原因,构建后的index.html
这个文件是由html-webpack-plugin
这个插件根据项目的index.html
生成的,所以首先就是看一下项目内html-webpack-plugin
是怎么配置的。
查看webpack配置
webpack配置都是被vue-cli封装好的,没有直接在项目中显示,但vue有提供一个命令可以查看webpack的所有配置,vue inspect
将配置在命令行中显示,但平常为了更好查看,都是将配置输出到一个文件中,所以可以这样子使用,vue inspect > config.js
,这样子我们就可以看到html-webpack-plugin
对应配置
对比vue配置
因为正常分包只需要在vue.config.js
中做好分包配置就可以,所以我对于vue项目也输出了一份html-webpack-plugin
配置,发现在vue中html-webpack-plugin
配置没有具体写导入的chunks
,所以我们的解决方式就是将html-webpack-plugin
配置中多余的chunks删除掉。
为什么删掉chunks配置就可以?
这个是html-webpack-plugin
中的一段代码,生成引入html中的chunk之前,会执行这个方法,过滤掉不需要在首页加载的chunks,也就是js。filterChunks
包含三个参数,chunks
——项目的所有chunk,includedChunks
——需要首页加载的所有chunk,没有配置的话,默认为all
,excludedChunks
——需要排除的chunk,默认为空数组。同样也会过滤!chunk.initial
也就是async
异步类型的chunk,我们在splitChunks
配置chunks
值为initial
和all
都满足需求。
接下来通过断点的方式查看一下filterChunks
三个参数的具体值,如下:
这样子就能得到我们想要的结论,虽然chunk-uview-ui
的initial
值为true,本来应该在index.html
中被引入,但是uni-app
配置了includedChunks
的值为chunk-vendors、chunk-common、index
,chunk-uview-ui
不包含在includedChunks
中,导致了它被过滤掉了,如果我们把html-webpack-plugin
插件配置的chunks
删掉,那includedChunks
的值就会变为默认的all
,Array.isArray(includedChunks)
为false,就不会过滤本来应该被引用的chunk了,这样子就解释了为什么删掉chunks配置,我们的分包结果就能被正常加载。
解决对应问题,怎么删掉chunks配置
chainWebpack: (config) => {
config.plugin('html-index').tap((options) => {
delete options[0].chunks;
return options;
});
}
- 我们其实也不知道
options
对应的接口,可以通过console.log
或者debugger
得到对应内容,可以看到options是个数组,包含了html-webpack-plugin
配置。
怎么知道是怎么修改呢,可以查看vue-cli文档,对象的修改插件选项方式
至于为什么在uni-app中是使用的插件名称是html-index
,而不是html
,我们这边本质修改的html-webpack-plugin
的配置,通过vue inspect
输出的结果可以看到怎么使用插件的备注config.plugin('html-index')
这边是html-index
而不是html
的原因,是因为uni-app
是使用多页面配置的方式,虽然平常只配置了一个页面,可以在@dcloudio/vue-cli-plugin-uni/lib/h5/index.html
查看配置