聊聊 webpack 異步加載(二):webpack 如何處理 import()

作者 | vayne
轉載請註明出處

解釋了webpack 是怎麼加載拆包後的代碼的,這篇文章解釋下 webpack 如何處理 import()。import() 包含的代碼被 webpack 當作 chunk 處理,同樣也是通過 window["webapckJsonp"] 來進行加載。有了上篇文章的講解,本篇理解起來會很容易。講解之前再回顧下 webpack bootstrap 代碼中非常重要的四個緩存變量

聊聊 webpack 異步加載(二):webpack 如何處理 import()

  • ​modules​:緩存 ​module​ 代碼塊,每個 ​module​ 有一個 ​id​,開發環境默認以 ​module​ 所在文件的文件名標識,生產環境默認以一個數字標識。​modules​ 是一個 ​object​, ​key​ 為 ​module id​,​value​ 為對應 ​module​ 的源代碼塊。
  • ​installedModules​:緩存已經加載過的 module​,簡單理解就是已經運行了源碼中 ​import somemodule from 'xxx'​ 這樣的語句。​installedModules​ 是一個 ​object​, ​key​ 為 ​module id​,​value​ 為對應 ​module​ 導出的變量。(跟 ​modules​ 的 ​value​ 是不一樣的,這裡的 ​value​ 保存的是 ​module​ 對應的代碼中 ​export​ 的變量)
  • ​installedChunks​:緩存已經加載過的 chunk​,簡單理解就是把其他 ​js​ 文件中的 ​chunk​ 包含的 ​modules​ 同步到了當前文件中。每個 ​chunk​ 有一個 ​id​,默認以一個數字標識。​installedChunks​ 也是一個對象,​key​ 為 ​chunk id​,​value​ 有四種情況:
    • undefined:chunk not loaded
    • null:chunk preloaded/prefetched
    • Promise:chunk loading
    • 0:chunk loaded
  • ​deferredModules​:緩存運行當前 ​web app​ 需要的 ​chunk id​ 以及入口 ​module id​(截圖中 299 標識入口 ​module​ 的 ​id​,0 和 1 標識運行必需的另外兩個 ​chunk​ 的 ​id​),比如,​react​ 和 ​react-dom​ 被單獨打包到了另外的 ​js​ 中,入口文件需要等待 ​react​ 和 ​react-dom​ 加載成功之後才能運行(這篇文章不涉及)。

import() --> __webpack_require__.e

首先看一下 import() 被轉換後的代碼

聊聊 webpack 異步加載(二):webpack 如何處理 import()

聊聊 webpack 異步加載(二):webpack 如何處理 import()

文件路徑被替換成了 chunkId,作為參數調用了__webpack_require__.e,重點研究下 __webpack_require__.e 的實現

聊聊 webpack 異步加載(二):webpack 如何處理 import()

再來重複下, ​installedChunks​ 一個對象,​key​ 為 ​chunk id​,​value​ 有四種情況:

    • undefined:chunk not loaded
    • null:chunk preloaded/prefetched(本篇不涉及)
    • Promise:chunk loading
    • 0:chunk loaded
  • 判斷 installedChunks[chunkId] 是否已經被加載,如果已經被加載,直接返回 Promise.all([])
  • 判斷 installedChunks[chunkId] 是否在加載中,如果在加載中,把表示加載中的 promise 添加到 promises 數組中
  • 沒有加載的話,創建 promise,並賦值:installedChunks[chunkId] = [resolve, reject],添加到promises 數組中
  • 動態創建>
聊聊 webpack 異步加載(二):webpack 如何處理 import()

    • 加載成功或者失敗都會清除加載超時的處理函數
    • 如果加載沒有成功,構造 error 信息。還記得 installedChunks[chunkId] = [resolve, reject] 嗎?這個時候就派上用場了,chunk[1](error) 即為 reject(error),觸發 promise 的 reject
  • __webpack_require__.e 返回 prmises 數組,等待 chunk 加載完成

webpackJsonpCallback

什麼時候 promise 會 resolve 呢?code spliting 加載代碼時,webpack 的 bootstrap 已經運行過,也就是說 window["webapckJsonp"] 的 push 方法已經被改寫成了 webpackJsonpCallback

聊聊 webpack 異步加載(二):webpack 如何處理 import()


聊聊 webpack 異步加載(二):webpack 如何處理 import()

現在以 code spliting 的角度再來走一遍下面的邏輯

聊聊 webpack 異步加載(二):webpack 如何處理 import()

  • 參數 data 的形式為 [ chunkId[], modules[] ]
  • 第一個 for 循環判斷 installedChunks[chunkId] 是否在加載中,這個時候 installedChunks[chunkId] = [resolve, reject]。執行當前函數時,代表當前 chunk 已經被正確加載,因此 installedChunks[chunkId][0] 表示的即為 resolve
  • 第二個 for 循環把當前 chunk 包含的 module 保存到入口文件的 modules 變量
  • 執行 resolve(),__webpack_require__.e 返回 fullfilled 態的 promise,表示 chunk 加載完畢
聊聊 webpack 異步加載(二):webpack 如何處理 import()

執行 __webpack_require__(moduId),加載入口代碼,從而 import() 邏輯加載完畢

依然畫個流程圖總結一下

聊聊 webpack 異步加載(二):webpack 如何處理 import()

相關鏈接:


分享到:


相關文章: