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

代碼執行時機將決定着是否可以正常執行,當依賴文件沒加載完成就開始執行、使用對應模塊,那麼將會致使執行異常。這在 「存在資源加載失敗時,加載重試影響原來文件的執行順序」 的場景下尤其常見。react

webpack 構建除了進行模塊依賴管理,實際上,也自然地管理了 entry 與 chunk 多文件的執行時機,但缺乏了對 external 文件管理,當 external 文件加載失敗或未完成時,執行、使用對應模塊一樣將致使異常。爲此,wait-external-webpack-plugin 應運而生,以 webpack 插件的形式,補充 external 的執行管理。本文將進行簡要說明。webpack

1、單文件

將 webpack 打包後的代碼進行簡化,其實就是一個當即調用函數;傳入 「模塊」,使用 webpack_require 進行調用。在單文件下,文件加載後將當即執行業務邏輯。web

(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__) {
        // ...
    })
});
複製代碼

2、多文件

爲了 「抽取公共模塊進行單獨打包避免重複加載」 或 「增長併發請求數減小總加載時間」 等緣由,通常會將代碼拆分紅多文件,可以使用以下形式:json

使用 webpack 的 splitChunks 插件,將代碼拆分紅多個 chunk 文件; 經過配置 external,將第三方庫單獨加載; 拆分紅多個文件後,爲了不業務邏輯執行時相關文件還沒加載完成致使執行出錯,須要等待相關文件都加載完成後再開始執行。緩存

2.1 等待 entry 與 chunk 文件都加載完成
entry 與 其餘 chunk 文件的 「等待-執行」 的邏輯,webpack 其實已經幫咱們自動生成了。bash

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 文件加載順序不會影響執行時機,只有在都加載完成後,纔會執行業務邏輯。以下圖示jsonp

2.2 等待 external 文件加載完成
項目引用第三方庫,通常會配置 external 讓庫單獨加載。經過 webpack 生成的代碼能夠看出,配置 external 的模塊在業務代碼執行前將被看成已存在環境中,不作任何判斷。因此當 external 文件未加載完成或加載失敗時,使用對應模塊將會致使執行出錯

"react":  (function(module, exports) {
     eval("(function() { module.exports = window[\"React\"]; }());");
})
複製代碼

2.2.1 添加等待 external 文件加載完成再執行邏輯
爲了不使用時出錯,在執行前需先保證 external 文件已經加載完成。處理方式以下ui

  • 將 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);
    }
})();
複製代碼

2.2.2 「自動」 生成等待 external 文件加載完成再執行邏輯
等待 external 加載完成邏輯是統一的,差別在於依賴的 external 或有不一樣。爲了不手動添加出錯,咱們能夠經過以 webpack 插件的形式自動分析依賴,並生成相關代碼。

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

具體實現可見插件 wait-external-webpack-plugin

經過 wait-external-webpack-plugin 插件,可以自動生成等待依賴的 external 文件加載完成再執行邏輯,對開發者透明,保證文件對正常執行。

歡迎使用,歡迎任何意見或建議,謝謝。

轉載自AlloyTeam:www.alloyteam.com/2019/07/wai…

相關文章
相關標籤/搜索