看這篇前,若是對瀏覽器緩存原理不太清楚的,先補習瀏覽器緩存這部分基礎知識。若是對文件摘要算法不太清楚的,先補習文件摘要算法這部分基礎知識。vue
用vue-cli搭建的項目,打包的時候會把node_modules目錄下全部文件都分離出來單獨生成vendor.js。node
// split vendor js into its own file new webpack.optimize.CommonsChunkPlugin({ name: "vendor", minChunks: function (module, count) { // any required modules inside node_modules are extracted to vendor return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, "../../node_modules") ) === 0 ); } }),
node_modules中的外部插件更新不頻繁,因此單獨生成文件能夠利用瀏覽器緩存提高頁面性能,這是個很是好的實踐。jquery
實際工做中,若是隻是修改已有組件中的代碼,打包後的vendor.js的hash碼是不會變的。But!只要我新寫一個組件而後引入項目,打包出來的全部文件的hash碼所有都會變動,包括vendor.js。webpack
參考https://webpack.js.org/guides...後,大概搞清楚了webpack緩存優化問題。web
假設如今項目就三個組件,只用到jquery。通常狀況下,全部的組件在webpack打包後,都會以數組項的形式保存下來:算法
// main.js裏包含三個組件,其對應的id爲1,2,3 webpackJsonp([0],[ /* 0 */, /* 1 */ (function(module, __webpack_exports__, __webpack_require__){...}), /* 2 */ (function(module, __webpack_exports__, __webpack_require__){...}), /* 3 */ (function(module, __webpack_exports__, __webpack_require__){...}) ],[1]); //jquery.js分離出來的vendor.js包含了兩個組件 webpackJsonp([1],[ /* 0 */ (function(module, exports, __webpack_require__) {...}), /* 1 */, /* 2 */, /* 3 */, /* 4 */ (function(module, exports, __webpack_require__) { module.exports = __webpack_require__(0); }) ],[4]);
因此在webpack內部,組件的調用,靠的是他們在數組中的index,項目中全部的組件都擁有一個惟一的index。當我新加一個組件進去的時候,main.js裏的數組會被擴充,而vendor.js裏的數組也會被重排,因此致使前面所說的問題:vue-cli
// 新增組件後,main.js裏包含四個組件,其對應的id爲1,2,3,4 webpackJsonp([0],[ /* 0 */, /* 1 */ (function(module, __webpack_exports__, __webpack_require__){...}), /* 2 */ (function(module, __webpack_exports__, __webpack_require__){...}), /* 3 */ (function(module, __webpack_exports__, __webpack_require__){...}), /* 4 */ (function(module, __webpack_exports__, __webpack_require__){...}) ],[1]); //jquery.js分離出來的vendor.js會被重排 webpackJsonp([1],[ /* 0 */ (function(module, exports, __webpack_require__) {...}), /* 1 */, /* 2 */, /* 3 */, /* 4 */, /* 5 */ (function(module, exports, __webpack_require__) { module.exports = __webpack_require__(0); }) ],[5]);
解決這一問題的方法官網推薦使用的是new webpack.HashedModuleIdsPlugin()這個插件,只要引入這個插件,問題就不存在了。好奇心讓我點進去看了下這個插件究竟幹了什麼,怎麼作到讓vendor.js不受其外部包的影響的?segmentfault
發現代碼異常短小精悍數組
const createHash = require("crypto").createHash; class HashedModuleIdsPlugin { constructor(options) { this.options = Object.assign({ hashFunction: "md5", hashDigest: "base64", hashDigestLength: 4 }, options); } apply(compiler) { const options = this.options; compiler.plugin("compilation", (compilation) => { const usedIds = new Set(); compilation.plugin("before-module-ids", (modules) => { modules.forEach((module) => { if(module.id === null && module.libIdent) { const id = module.libIdent({ context: this.options.context || compiler.options.context }); // 獲取數據摘要方法,默認md5 const hash = createHash(options.hashFunction); // 將模塊id進行摘要算法 hash.update(id); // 再進行base64轉碼 const hashId = hash.digest(options.hashDigest); let len = options.hashDigestLength; // 判斷一下生成出來的base64前4位編碼,是否和前面的組件有衝突,有的話就再加一位,直到沒衝突了爲止,保證每一個組件擁有惟一的編碼。 while(usedIds.has(hashId.substr(0, len))) len++; // 將最終生成的編碼賦值給module.id module.id = hashId.substr(0, len); // 將module.id保存到usedIds列表裏,供後面執行的組件判斷是否編碼衝突 usedIds.add(module.id); } }); }); }); } } module.exports = HashedModuleIdsPlugin;
因此,就是用這樣簡單的方式,把原來的數據結構,換成了對象結構。用HashedModuleIdsPlugin插件打包後的vendor.js代碼變成了這樣:瀏覽器
//jquery.js分離出來的vendor.js會被重排 webpackJsonp([1],{ /***/ 0: /***/ (function(module, exports, __webpack_require__){...}), /***/ "tra3": /***/ (function(module, exports, __webpack_require__) {...}) },[0]);
這樣就擺脫了數組引發的序號重排問題,從而保證只要jquery內部代碼不變,這個vendor.js的hash就不會變。