webpack學習筆記-緩存優化

看這篇前,若是對瀏覽器緩存原理不太清楚的,先補習瀏覽器緩存這部分基礎知識。若是對文件摘要算法不太清楚的,先補習文件摘要算法這部分基礎知識。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就不會變。

相關文章
相關標籤/搜索