在單頁應用中,常常使用 webpack 的 動態導入 功能來異步加載模塊,從而減小部分文件的體積。咱們能夠經過webpack 提供的 import()
和 require.ensure
兩個 API 來使用該功能。因爲兩個方法根本實現都是相同的,本文的示例都基於 import()
方法。javascript
webpack 4.35.3
java
這邊用一個最簡單的例子,有兩個文件,
index.js
爲入口文件,該文件使用import()
來動態導入async.js
文件。webpack
index.js
web
async.js
promise
咱們執行 webpack --mode=development
來得到編譯後文件 main.js
與 0.js
。其中,main.js
包含 index.js
代碼,0.js
包含 async.js
代碼。bash
首先,main.js
文件做爲整個應用的入口,咱們來看看裏面有什麼東西。markdown
咱們先忽略詳細的代碼邏輯,總體看下來,發現這個文件其實就是一個自執行函數,該函數把 轉化後的 index.js
和 文件路徑 組成一個 modules 傳給主函數。異步
主函數中,會執行 __webpack_require__(__webpack_require__.s = "./src/index.js");
來初始化入口模塊。async
這個函數的做用就是加載而且執行指定的模塊,而且返回模塊的 module.exports
。 這邊執行模塊調用了 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
來執行以前傳入的 module,也就是 轉換後的index.js
。函數
咱們抽離當中的代碼看一下。
發現import
被轉化成了 __webpack_require__.e(/*! import() */ 0)
,咱們順藤摸瓜,繼續來看看 __webpack_require__.e
作了些什麼。
代碼可能有點眼花,看下來無非就是作了這麼一件事情。
installedChunks
檢查是否加載過該 chunkJSONP
請求去加載 chunk當 Promise 返回以後,就會繼續執行咱們以前的異步請求回調
__webpack_require__.e(/*! import() */ 0) .then( __webpack_require__.bind(null, /*! ./async */ "./src/async.js") )... 複製代碼
這裏直接調用了 __webpack_require__
去加載咱們的 異步模塊
。
這裏就有兩個問題?
__webpack_require__
是根據咱們以前傳入的 modules
來獲取 module
的,可是,在 __webpack_require__.e
中並無看到有對 modules
執行操做的代碼。那 modules
究竟是何時被更新的呢?promise
把 resolve
和 reject
所有存入了 installedChunks
中, 並無在獲取異步chunk成功的onload
回調中執行 resolve
,那麼,resolve
是何時被執行的呢?var promise = new Promise(function(resolve, reject) { installedChunkData = installedChunks[chunkId] = [resolve, reject]; }); 複製代碼
帶着疑問,咱們來看一下加載的 0.js
中的內容。
0.js
這邊邏輯也很簡單,只是往window["webpackJsonp"]
裏面 push chunkId
和 含有的modules
。咱們搜一下 window["webpackJsonp"]
, 發如今 main.js
中有這麼一段代碼。
這邊用自定義的 webpackJsonpCallback
函數替換了 window["webpackJsonp"]
的 push
方法。因此說,咱們以前 0.js
執行的 push
其實就是執行了自定義的 webpackJsonpCallback
函數。
能夠看到,webpackJsonpCallback 作了2件事情。
installedChunks
中的 resolve
, 讓 import()
得以繼續執行。chunk
中含有的 模塊所有註冊到 modules
變量中。如今,咱們終於理清了異步加載的所有流程。
其實,以上的代碼只是最簡單的狀況,隨着代碼的不一樣,生成的函數具體內容也會有所差別。好比,咱們添加預加載代碼 import(/* webpackPrefetch: true */'./preload');
, 生成的 main.js
文件中才會有支持該功能的代碼,不難看出,webpack此舉是爲了控制生成對文件大小,對具體細節感興趣的同窗,能夠本地嘗試一下。