近来遇项目打包之事,撰文记之。以期分享,皆有所获。html
前提有两点,须要获得你的认同:vue
【后台管理系统】框架和 UI 组件库最强组合为 vue-element-admin + Element UI!(●'◡'●)node
webpack4 最核心的特性是 【splitChunks】,splitChunks 最核心的配置是 cacheGroups!webpack
基于这个两个前提,咱们再进行下一步。web
webpack 打包分析有它就够了:webpack-bundle-analyzernpm
npm install --save-dev webpack-bundle-analyzer复制代码
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 的设置比较重要。element-ui
npm run dev 或 npm run build复制代码
http://127.0.0.1:8877复制代码
看一下我们的打包分析图:json
得出如上图的分包并不难,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 } } })复制代码
若是你暂时还看不懂这些配置项,先别急,后面会一一陈述。app
你只用先知道:它拆了初始化加载的第三方包、拆了 Element UI 库、拆了 src/components。
一切彷佛好像还不错,可是咱们并不知足。
实际上,我们跑一下 npm run build:test 也会报警告。
那么还有哪些点能够继续优化?结合以上分析图和 test warning,很明显,咱们须要思考:
淦!打包什么的,多打几遍就完事了。十遍不行就一百遍,一百遍不行就一千遍,一千遍不行就......
淦完后得出以下打包分析图:
本瓜成功的将打包大小从 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 最关键的配置:
【重要】
chunk 的文件名
过滤 modules,默认为全部的 modules,可匹配模块路径或 chunk 名字,当匹配到某个 chunk 的名字时,这个 chunk 里面引入的全部 module 都会选中;
权重,数字越大表示优先级越高。一个 module 可能会知足多个 cacheGroups 的正则匹配,到底将哪一个缓存组应用于这个 module,取决于优先级;
有三个值:all、async、initial。
这里是一段示例代码,来看看设置不一样的 chunks,它们有什么样的打包区别:
//app.js import "my-statis-module"; if(some_condition_is_true){ import ("my-dynamic-module") } console.log("My app is running")复制代码
asyn : (default)
会生成两个打包文件:
initial :
会生成三个打包文件:
all :
会生成两个打包文件:
设置 "all" 大小将最小,区别使用这三者,是核心中的核心。
【了解】
表示被拆分出的 bundle 在拆分以前的体积的最小数值,只有 >= minSize 的 bundle 会被拆分出来;
表示被拆分出的 bundle 在拆分以前的体积的最大数值,默认值为 0,表示 bundle 在拆分前的体积没有上限;maxSize 若是为非 0 值时,不能小于 minSize;
表示在分割前,可被多少个chunk分享的最小值
表示是否使用已有的 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 的包文件。
从打包的结果去检验代码,也是一种不错的优化手段!
基于本次分包,本瓜简单梳理一下策略:
包大小 | 策略 |
---|---|
0 KB 至 10 KB | 合并包 |
10 KB 至 100 KB | 大小合适 |
100 KB 至 200 KB | 核心包,重点关注 |
大于 200 KB | 考虑拆包 |
特殊状况 | 特殊处理 |
本次就先到这,打包无止境,愿这个世上没有打包攻城狮。