使用webpack3进行项目打包,有三大需求:
1. 多页面
我的项目有前台和后台两个入口,可以看成是两个独立的SPA应用,但是有公共的第三方库,像react、redux、react-router4,所以需要将这些第三方库抽离出来。项目中自身是有自己写的公共代码模块,也是一样要抽离出来。这个使用CommonsChunkPlugin插件。
2. 代码分割
项目中的设计是,一个基于react-router4的路由方式,使用bundle-loader,还有一个是react的组件方式,使用import()方法。这样可以合理的减少打包体积。
3. 缓存
第三方打包出来的vendor.js 的hash值最好不要变,只变动更改过的代码部分。
使用清单文件(Manifest File),为什么呢?
但是,如果我们可以修改应用的代码并且再次执行 webpack 命令,我们看到 vendors 文件的 hash 还是变化了。即使我们已经分离了 vendor 和 main 代码包,但是当应用代码发生修改的时候 vendor 还是变化了。 这意味着我们依旧不能享受浏览器缓存带来的好处,因为每一次重新编译都会修改 vendors 的 hash 值。
这个问题是因为每一次编译,webpack 生成一些 webpack 运行时代码,用来帮助 webpack 来做它的工作。当那里存在一个单独的代码包,运行时会驻留在其中。但当多个代码包被生成的时候,运行时代码会被提取到公共的模块中,就是这个 vendor 文件。
为了阻止这个,我们需要提取出运行时到一个分离的清单文件(Manifest File)。虽然我们又多创建另一个代码包,但它的开销也被我们在 vendor 文件上获得的长期缓存所带来的好处所抵消了。
上文引用说到这个hash值,其是错误的,经过我的研究,我在这里直接抛出结论:
- 一个打包后的文件都有一个chunkhash
- 每次打包webpack都会生成一个hash
hash和chunkhash是两个不同的东西,这么来说,hash指的是总的hash,是每次打包都会生成的,一旦某一个代码发生改变,新的hash就会生成,而且都是一样的,所以用这个做缓存是没有意义的,chunkhash指的是单个文件的hash值,生成的每一个文件都是不一样的,一旦改变,只改变自身,与其他的文件没有任何关系。
所以配置上要这样做:
output: {
filename: "bundle-[name]-[chunkhash:4].js",
chunkFilename: '[id]-[chunkhash:4].bundle.js', // 代码分割
path: path.resolve(__dirname, 'dist'),
},
但是现在有个问题,比如a文件引用了react等第三方库,我通过CommonsChunkPlugin配置将react等抽离出来,生成一个叫vendor.js文件,一旦我改变a文件,vendor的hash值也会发生改变,这是不应该的,因为我要做缓存,这里的vendor不是业务代码,不会发生改变的。为什么呢?看上文那个引用的文字,意思是说生成的vendor会有一些webpack的代码,这个代码在打包时会改变,所以要抽离出来,形成一个mainfest文件,每次打包之只改变mainfest文件的chunkhash值,vendor不变。
配置:
new webpack.optimize.CommonsChunkPlugin({
name: ['vendor', 'manifest'],
}),
生成的manifest文件的代码:
var parentJsonpFunction = window["webpackJsonp"];
/******/ window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
/******/ // add "moreModules" to the modules object,
/******/ // then flag all "chunkIds" as loaded and fire callback
/******/ var moduleId, chunkId, i = 0, resolves = [], result;
/******/ for(;i < chunkIds.length; i++) {
/******/ chunkId = chunkIds[i];
/******/ if(installedChunks[chunkId]) {
/******/ resolves.push(installedChunks[chunkId][0]);
/******/ }
/******/ installedChunks[chunkId] = 0;
/******/ }
/******/ for(moduleId in moreModules) {
/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/ modules[moduleId] = moreModules[moduleId];
/******/ }
/******/ }
/******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);
/******/ while(resolves.length) {
/******/ resolves.shift()();
/******/ }
/******/ if(executeModules) {
/******/ for(i=0; i < executeModules.length; i++) {
/******/ result = __webpack_require__(__webpack_require__.s = executeModules[i]);
/******/ }
/******/ }
/******/ return result;
/******/ };
代码里的chunkhash就是对于vendor的引用。
参考:
【翻译向】webpack2 指南(上)