在前端工程的的打包史中,common文件向来都不是一个好处理的方面。在这一块,webpack提供了CommonsChunkPlugin来处理这个事情,可是在因为文档的模棱两可,再加上各类配置选项的多样性和某些bug,仍是有很多坑的。javascript
所谓工欲善其事必先利其器,咱们既然想作common方面的优化,那么首先确定要知道打包后的文件体积庞大的主要缘由。说到这里就不得不提到一个至关好用的工具:webpack-bundle-analyzerhtml
它既是一个webpack插件,又是一个命令行工具。可以将webpack包的内容转换成可缩放的树状图,方便进行交互分析。恩。。。就是这玩意:前端
npm install --save-dev webpack-bundle-analyzer
在 webpack.config.js
中:java
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // ... plugins: [new BundleAnalyzerPlugin()] // ...
默认配置以下:node
new BundleAnalyzerPlugin({ // Can be `server`, `static` or `disabled`. // In `server` mode analyzer will start HTTP server to show bundle report. // In `static` mode single HTML file with bundle report will be generated. // In `disabled` mode you can use this plugin to just generate Webpack Stats JSON file by setting `generateStatsFile` to `true`. analyzerMode: 'server', // Host that will be used in `server` mode to start HTTP server. analyzerHost: '127.0.0.1', // Port that will be used in `server` mode to start HTTP server. analyzerPort: 8888, // Path to bundle report file that will be generated in `static` mode. // Relative to bundles output directory. reportFilename: 'report.html', // Automatically open report in default browser openAnalyzer: true, // If `true`, Webpack Stats JSON file will be generated in bundles output directory generateStatsFile: false, // Name of Webpack Stats JSON file that will be generated if `generateStatsFile` is `true`. // Relative to bundles output directory. statsFilename: 'stats.json', // Options for `stats.toJson()` method. // For example you can exclude sources of your modules from stats file with `source: false` option. // See more options here: https://github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21 statsOptions: null, // Log level. Can be 'info', 'warn', 'error' or 'silent'. logLevel: 'info' })
第一步:webpack
webpack --profile --json > stats.json
第二步:git
webpack --profile --json | Out-file 'stats.json' -Encoding OEM
执行成功后,你将看到如下动态网页:github
在这里顺便放上线上文件加载waterfall,做为对比。web
经过图表能够看到,在如下配置下:算法
config.plugins.push(new CommonsChunkPlugin('commons', 'js/commons.[hash].bundle.js'));
打包出来的文件仍是有不少问题的:
common包规划不合理, swiper.js ,area.json等公用文件大量重复加载。
antd 没有抽离出来,没法并行加载,也没法进一步作运行时按需加载。
echarts在每一个使用的包都单独打包一份,只要包含echarts的包,基本一百多kb。(线上压缩并开启gzip)
import {ImgBigSwiper} from 'components/src/index';
这种写法会致使将components里面的全部组件所有打包进页面的js。应该这样写:import ImgBigSwiper from 'components/src/ImgBigSwiper';
挨个引入,见webpack将ES6编译成CommonJs后只引入用到的模块
common.js独占490kb,要等这个包加载完后index才开始解析路由。
在这个过程当中,会发现一个有趣的事情。就是index.html页面的script加载分为如下两个部分:
...... <script type="text/javascript" src="//res.dinghuo123.com/src/common/ueditor/ueditor.config.js"></script> <script type="text/javascript" src="//res.dinghuo123.com/src/common/ueditor/ueditor.config.js"></script> <script type="text/javascript" src="//res.dinghuo123.com/src/common/ueditor/ueditor.config.js"></script> ...... <script> document.write('<script src="https://resource.dinghuo123.com/dist/ydhv2/webpack.assets.js?v=' + Math.random() + '"><\/script>'); document.write('<script src="' + window.WEBPACK_ASSETS['commons'].js + '"><\/script>'); document.write('<script src="//res.dinghuo123.com/src/common/ueditor/ueditor.config.js"><\/script>'); document.write('<script src="' + window.WEBPACK_ASSETS['index'].js + '"><\/script>'); </script>
而后你会发现,是上面一块的script并行加载完,才并行加载下一个script标签的内容。你们能够思考一下为何。
进过调整以后的CommonsChunkPlugin配置:
config.plugins.push(new CommonsChunkPlugin({ name: 'commons', minChunks: Infinity // 随着 入口chunk 愈来愈多,这个配置保证没其它的模块会打包进 公共chunk })); config.plugins.push(new CommonsChunkPlugin({ async:'antd', minChunks(module) { var context = module.context; return context && context.indexOf('antd/dist') >= 0; } })); config.plugins.push(new CommonsChunkPlugin({ async:'echarts', minChunks(module) { var context = module.context; return context && (context.indexOf('echarts') >= 0 || context.indexOf('zrender') >= 0); } }));
这里用到了minChunks和async两个配置。
其中第一name的commons
是一个entry
入口,里面是一个依赖包的数组。minChunks
设置为Infinity
这个配置保证没其它的模块会打包进 公共chunk。由于说实话,CommonsChunkPlugin的commons分析实在是不怎么只能,仍是手动控制会更好一些。
固然,你能够传入一个 function
,以添加定制的逻辑(默认是 chunk 的数量),这个函数会被 CommonsChunkPlugin 插件回调,而且调用函数时会传入 module 和 count 参数。
module 参数表明每一个 chunks 里的模块,这些 chunks 是你经过 name/names 参数传入的。
module.context: The directory that stores the file. For example: '/my_project/node_modules/example-dependency'
module.resource: The name of the file being processed. For example: '/my_project/node_modules/example-dependency/index.js'
count 参数表示 module 被使用的 chunk 数量。
当你想要对 CommonsChunk 如何决定模块被打包到哪里的算法有更为细致的控制, 这个配置就会很是有用。
下面的内容是官网弄过来的,其实我也看不太懂。。。
若是设置为
true
,一个异步的 公共chunk 会做为options.name
的子模块,和options.chunks
的兄弟模块被建立。它会与options.chunks
并行被加载。能够经过提供想要的字符串,而不是true
来对输出的文件进行更换名称。
仍是先看打包分析的结果吧:
经过上面分析能够看到:
common合理划分,抓大放小。
antd和echarts提取出来,并行加载。
components 用到什么打包什么。(手动控制的)
大大减少了其余业务包的体积,93%的业务包大小控制在25K之内,剩下7%的业务包大小控制在50k之内。(开启gzip)
首屏加载资源的总大小几乎没有变化。
echarts 和Ueditor运行时按需加载。
tree-shaking
的探索
Vendor and code splitting in webpack 2
webpack 按需打包加载
weboack Split app and vendor code
awesome-webpack-cn