作者 | vayne
轉載請註明出處
解釋了webpack 是怎麼加載拆包後的代碼的,這篇文章解釋下 webpack 如何處理 import()。import() 包含的代碼被 webpack 當作 chunk 處理,同樣也是通過 window["webapckJsonp"] 來進行加載。有了上篇文章的講解,本篇理解起來會很容易。講解之前再回顧下 webpack bootstrap 代碼中非常重要的四個緩存變量
- 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() 被轉換後的代碼
文件路徑被替換成了 chunkId,作為參數調用了__webpack_require__.e,重點研究下 __webpack_require__.e 的實現
再來重複下, 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 數組中
- 動態創建>
- 加載成功或者失敗都會清除加載超時的處理函數
- 如果加載沒有成功,構造 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
現在以 code spliting 的角度再來走一遍下面的邏輯
- 參數 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_require__(moduId),加載入口代碼,從而 import() 邏輯加載完畢
依然畫個流程圖總結一下
相關鏈接:
閱讀更多 頭號前端 的文章