配置:因为 vue-element-admin 基于 vueCli4,所以在 vue.config.js chainWebpack 中设置
config.plugin('BundleAnalyzerPlugin').use(BundleAnalyzerPlugin).tap(() => [
rel: 'BundleAnalyzerPlugin' ,
analyzerMode: 'server' , // 'server': 启动端口服务;'static': 生成 report.html;'disabled': 配合 generateStatsFile 使用;
generateStatsFile: false , // 是否生成stats.json文件
analyzerHost: '127.0.0.1' ,
analyzerPort: '8877' ,
reportFilename: 'report.html' ,
defaultSizes: 'parsed' ,
openAnalyzer: false ,
statsFilename: 'stats.json' ,
statsOptions: null ,
excludeAssets: null
其中 analyzerMode 的设置比较重要。
npm run dev 或 npm run build
http:
看一下咱们的打包分析图:
得出如上图的分包并不难,vue-element-admin 自带这些配置。
config.optimization.splitChunks ({
chunks : 'all' ,
cacheGroups : {
libs : {
name : 'chunk-libs' ,
test : /[\\/]node_modules[\\/]/,
priority : 10 ,
chunks : 'initial' // only package third parties that are initially dependent
elementUI : {
name : 'chunk-elementUI' , // split elementUI into a single package
priority : 20 , // the weight needs to be larger than libs and app or it will be packaged into libs or app
test : /[\\/]node_modules[\\/]_?element-ui (.*)/ // in order to adapt to cnpm
commons : {
name : 'chunk-commons' ,
test : resolve ('src/components' ), // can customize your rules
minChunks : 3 , // minimum common number
priority : 5 ,
reuseExistingChunk : true
如果你暂时还看不懂这些配置项,先别急,后面会一一陈述。
你只用先知道:它拆了初始化加载的第三方包、拆了 Element UI 库、拆了 src/components。
一切似乎好像 还不错,但是我们并不满足。
实际上,咱们跑一下 npm run build:test
也会报警告。
那么还有哪些点可以继续优化?结合以上分析图和 test warning,很明显,我们需要思考:
Echarts 的体积大小不能忽视,如何处理它?是首页加载还是异步加载?要按需引入吗?
vue.js 等库还能不能再拆?
首页 Entrypoints 所依赖的包还能不能再优化?
包的体积应控制在什么范围?包太大,加载会太慢!包太小,会消耗 HTTP 请求连接!更多:合并 HTTP 请求是否真的有意义?
更多......
淦!打包什么的,多打几遍就完事了。十遍不行就一百遍,一百遍不行就一千遍,一千遍不行就......
优化的结果
淦完后得出如下打包分析图:
本瓜成功的将打包大小从 3.1MB 变成了 2.36MB ,文件数从 68个 打包到了 43个 !!! ,既实现了拆包 (拆公共库),也实现了并包 (合并极小的包)。
虽然这不会是最终的结果,但本瓜可以先下一个结论:
配置 cacheGroups,权衡拆包与并包二者,便是 webpack 分包的究极奥义!
以下是 cacheGroups 配置:
config.optimization.splitChunks ({
chunks : 'all' ,
cacheGroups : {
vue : {
name : 'chunk-vuejs' ,
test : /[\\/]node_modules[\\/]_?vue (.*)/,
priority : 20
elementUI : {
name : 'chunk-elementUI' , // split elementUI into a single package
priority : 30 , // the weight needs to be larger than libs and app or it will be packaged into libs or app
test : /[\\/]node_modules[\\/]_?element-ui (.*)/ // in order to adapt to cnpm
commons : { // split async commons chunk
name : 'chunk-async-commons' ,
minChunks : 2 ,
priority : 40 ,
chunks : 'async'
echarts : { // split echarts libs
name : 'chunk-echarts' ,
test : /[\\/]node_modules[\\/]_?echarts (.*)/,
priority : 50 ,
chunks : 'async'
zrender : { // split zrender libs
name : 'chunk-zrender' ,
test : /[\\/]node_modules[\\/]_?zrender (.*)/,
priority : 55 ,
chunks : 'async'
'manage-sendMsg' : { // resolve src
name : 'chunk-manage-sendMsg' ,
test : resolve ('src/views/manage/sendMsg' ),
priority : 80 ,
chunks : 'async'
'manage-packageLink' : { // resolve src
name : 'chunk-manage-packageLink' ,
test : resolve ('src/views/manage/packageLink' ),
priority : 80 ,
chunks : 'async'
......
其实咱单独从配置上去做优化,可操作的空间并不大。我们还应该从打包分析结果去回看我们的代码细节 ,调整业务代码来优化打包结果,或许是最直接有效的优化思路之一。包括:如何整合或解耦业务?如何做组件化?组件怎么引?插件怎么引?引多少?......每个点都能再操作操作!
CommonJs(require) ES6(import) 输出的是一个值的拷贝 输出的是值的引用 运行时加载 编译时输出接口
cacheGroups
下面我们再具体看看 cacheGroups 最关键的配置:
chunk 的文件名
过滤 modules,默认为所有的 modules,可匹配模块路径或 chunk 名字,当匹配到某个 chunk 的名字时,这个 chunk 里面引入的所有 module 都会选中;
priority
权重,数字越大表示优先级越高。一个 module 可能会满足多个 cacheGroups 的正则匹配,到底将哪个缓存组应用于这个 module,取决于优先级;
chunks (非常非常非常重要)
有三个值:all、async、initial。
这里是一段示例代码,来看看设置不同的 chunks,它们有什么样的打包区别:
import "my-statis-module" ;
if (some_condition_is_true){
import ("my-dynamic-module" )
console .log ("My app is running" )
asyn : (default)
会生成两个打包文件:
bundle.js (包含 app.js + my-statis-module)
chunk.js (只包含 my-dynamic-module)
initial :
会生成三个打包文件:
app.js (只包含 app.js)
bundle.js (只包含 my-static-module)
chunk.js (只包含 my-dynamic-module)
all :
会生成两个打包文件:
app.js (只包含 app.js)
bundle.js (包含 my-static-module + my-dynamic-module)
设置 "all" 大小将最小,区别使用这三者,是核心中的核心。
minSize
表示被拆分出的 bundle 在拆分之前的体积的最小数值,只有 >= minSize 的 bundle 会被拆分出来;
maxSize
表示被拆分出的 bundle 在拆分之前的体积的最大数值,默认值为 0,表示 bundle 在拆分前的体积没有上限;maxSize 如果为非 0 值时,不能小于 minSize;
minChunks
表示在分割前,可被多少个chunk分享的最小值
reuseExistingChunk
表示是否使用已有的 chunk,true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的,即几个 chunk 复用被拆分出去的一个 module;
代码层面:这样写,user.png 会被单独打成一个包。打包出来 148B ,属实没必要!
<img v-show ="imageUrl" :src="imageUrl" class="sort-img" >
<img v-show ="!imageUrl" src="~@/assets/user.png" class="sort-img" >
如果改成这样,则不会再被单独打包了。
<img :src ="imageUrl?imageUrl:'~@/assets/user.png'" class="sort-img" >
回看其它代码,本瓜发现所有在条件判断里面引入的文件都会被单独打包。然而它们其中有些是可以调整写法的,真没必要将几 KB 的文件单独打包成一个几 B 的包文件。
从打包的结果去检验代码,也是一种不错的优化手段!
基于本次分包,本瓜简单梳理一下策略:
公共的库是一定要尽量拆的。
公共的库尽量做到按需加载,这也是优化首屏加载需要注意的。
分包不能太细,0KB 至 10 KB 的包是极小的包,应当考虑合并。10 KB 至 100 KB 的包是小包,比较合适;100 KB 至 200 KB 的包只能是比较核心重要的包,需要重点关注,大于 200KB 的包就需要考虑拆包了。当然,也不排除一些特殊情况。
包大小 策略 0 KB 至 10 KB 合并包 10 KB 至 100 KB 大小合适 100 KB 至 200 KB 核心包,重点关注 大于 200 KB 考虑拆包 特殊情况 特殊处理
本次就先到这,打包无止境,愿这个世上没有打包攻城狮。
关注公众号【掘金安东尼】,你的三连,我的动力!!!
掘金安东尼
Webpack
25.6w
腾讯IVWEB团队
JavaScript
Promise
9738
蚂蚁集团数据体验技术
Webpack
Node.js
15.9w
chenhongdong
JavaScript
18.8w
TianTianUp
Webpack
JavaScript
3.3w
LeoShaoQiang丶
Webpack
掘金·日新计划