1. 程式人生 > 實用技巧 >Webpack 打包後代碼執行時機分析與優化

Webpack 打包後代碼執行時機分析與優化

程式碼執行時機將決定著是否能夠正常執行,當依賴檔案沒載入完成就開始執行、使用對應模組,那麼將會導致執行異常。這在“存在資源載入失敗時,載入重試影響原來檔案的執行順序”的場景下尤為常見。

webpack構建除了進行模組依賴管理,實際上,也天然地管理了 entry 與 chunk 多檔案的執行時機,但缺少了對 external 檔案管理,當 external 檔案載入失敗或未完成時,執行、使用對應模組同樣將導致異常。為此,wait-external-webpack-plugin應運而生,以webpack外掛的形式,補充 external 的執行管理。本文將進行簡要說明。

一、單檔案

將 webpack 打包後的程式碼進行簡化,其實就是一個立即呼叫函式;傳入“模組”,使用webpack_require

進行呼叫。在單檔案下,檔案載入後將立即執行業務邏輯。

(function(modules) { // webpackBootstrap
     function __webpack_require__(moduleId) {
        // ...
	// 執行模組程式碼
	modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
     }
     // 引用入口
     return __webpack_require__(__webpack_require__.s = "./src/entryB.js
"); })({ "./entryB.js": (function(module, __webpack_exports__, __webpack_require__) { // ... }) });

二、多檔案

為了 “抽取公共模組進行單獨打包避免重複載入” 或 “增加併發請求數減少總載入時間” 等原因,一般會將程式碼拆分成多檔案,可使用如下形式:

  • 使用 webpack 的 splitChunks 外掛,將程式碼拆分成多個 chunk 檔案;
  • 通過配置 external,將第三方庫單獨載入;

拆分成多個檔案後,為了避免業務邏輯執行時相關檔案還沒載入完成導致執行出錯,需要等待相關檔案都載入完成後再開始執行。

2.1 等待 entry 與 chunk 檔案都載入完成

entry 與 其他 chunk 檔案的 “等待-執行” 的邏輯,webpack 其實已經幫我們自動生成了。

2.1.1 在生成的 entry 檔案中

  • 聲明瞭依賴的 chunk 檔案列表
  • 當 chunk 檔案載入後進行標記完成
  • 檔案載入後將檢查相關檔案是否都載入完成,如是,則開始執行業務邏輯
  • 提供給 chunk 檔案載入後的回撥方法
// # entry.js

// 宣告依賴列表
deferredModules.push(["./src/entryA.js","commons"]);

// 快取已完成的載入
var installedChunks = {
    "entryA": 0
};

function webpackJsonpCallback(data) {
    // 載入後標記完成
    installedChunks[chunkId] = 0;
}

// 檢查是否都載入完成,如是,則開始執行業務邏輯
function checkDeferredModules() {
    // 判斷 installedChunks 是否完整
    // ...
    if(fulfilled) {
        // 所有都載入,開始執行
        result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
    }
}

// 提供給 chunk 的全域性回撥方法
var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
jsonpArray.push = webpackJsonpCallback;

2.1.2 在生成的 chunk 檔案中

chunk 檔案載入後,正常情況下將呼叫 entry 提供的全域性回撥方法,標記載入完成。而當 chunk 檔案先於 entry 載入完成,則會先快取記錄,等 entry 檔案載入後讀取快取並將其標記完成。

// # chunk.js

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["commons"],{
     "./src/moduleA.js":  (function(module, __webpack_exports__, __webpack_require__) {
           // ...
     })
}]);

2.1.3 小結

基於以上分析,可以看出 entry 和 chunk 檔案載入順序不會影響執行時機,只有在都載入完成後,才會執行業務邏輯。如下圖示

2.2 等待 external 檔案載入完成

專案引用第三方庫,一般會配置 external 讓庫單獨載入。通過 webpack 生成的程式碼可以看出,配置 external 的模組在業務程式碼執行前將被當作已存在環境中,不做任何判斷。所以當 external 檔案未載入完成或載入失敗時,使用對應模組將會導致執行出錯。

"react":  (function(module, exports) {
     eval("(function() { module.exports = window[\"react\"]; }());");
})

2.2.1 新增等待 external 檔案載入完成再執行邏輯

為了避免使用時出錯,在執行前需先保證 external 檔案已經載入完成。處理方式如下

  • 將 entry 邏輯進行封裝,不立即執行
  • external 模組不存在時,則監聽等待檔案載入完成後再判斷執行
  • external 模組都存在後再執行 entry 邏輯

示意程式碼:

(function () {
    var entryInit = function () {
        (function(modules) {
            // webpackBootstrap
            //  ...
        })({})
    };
    if (window["React"]) {
        entryInit();
    } else {
        var hasInit = false; 
        var callback = function () {
            if(hasInit) return;
            if (window["React"]) {
                hasInit = true;
                document.removeEventListener('load', callback, true);
                entryInit();
            }
        };
        document.addEventListener('load', callback, true);
    }
})();

資源搜尋網站大全 http://www.szhdn.com 廣州VI設計公司https://www.houdianzi.com

2.2.2 “自動”生成等待 external 檔案載入完成再執行邏輯

等待 external 載入完成邏輯是統一的,差異在於依賴的 external 或有不同。為了避免手動添加出錯,我們可以通過以 webpack 外掛的形式自動分析依賴,並生成相關程式碼。

  • 獲取依賴的 external Modules
  • 分析 external 對應變數
  • 生成並注入相關邏輯程式碼

具體實現可見外掛wait-external-webpack-plugin

通過wait-external-webpack-plugin外掛,能夠自動生成等待依賴的 external 檔案載入完成再執行邏輯,對開發者透明,保證檔案對正常執行。