摘要:在過程中會利用簡稱中的兩個方法和。是通過請求最新的模塊代碼,然后將代碼返回給,會根據返回的新模塊代碼做進一步處理,可能是刷新頁面,也可能是對模塊進行熱更新。該方法返回的就是最新值對應的代碼塊。
Hot Module Replacement(簡稱 HMR)
包含以下內容:
熱更新圖
熱更新步驟講解
第一步:webpack 對文件系統進行 watch 打包到內存中webpack-dev-middleware 調用 webpack 的 api 對文件系統 watch,當文件發生改變后,webpack 重新對文件進行編譯打包,然后保存到內存中。
webpack 將 bundle.js 文件打包到了內存中,不生成文件的原因就在于訪問內存中的代碼比訪問文件系統中的文件更快,而且也減少了代碼寫入文件的開銷。
這一切都歸功于memory-fs,memory-fs 是 webpack-dev-middleware 的一個依賴庫,webpack-dev-middleware 將 webpack 原本的 outputFileSystem 替換成了MemoryFileSystem 實例,這樣代碼就將輸出到內存中。
webpack-dev-middleware 中該部分源碼如下:
// compiler // webpack-dev-middleware/lib/Shared.js var isMemoryFs = !compiler.compilers && compiler.outputFileSystem instanceof MemoryFileSystem; if(isMemoryFs) { fs = compiler.outputFileSystem; } else { fs = compiler.outputFileSystem = new MemoryFileSystem(); }第二步:devServer 通知瀏覽器端文件發生改變
在啟動 devServer 的時候,sockjs) 在服務端和瀏覽器端建立了一個 webSocket 長連接,以便將 webpack 編譯和打包的各個階段狀態告知瀏覽器,最關鍵的步驟還是 webpack-dev-server 調用 webpack api 監聽 compile的 done 事件,當compile 完成后,webpack-dev-server通過 _sendStatus 方法將編譯打包后的新模塊 hash 值發送到瀏覽器端。
// webpack-dev-server/lib/Server.js compiler.plugin("done", (stats) => { // stats.hash 是最新打包文件的 hash 值 this._sendStats(this.sockets, stats.toJson(clientStats)); this._stats = stats; }); ... Server.prototype._sendStats = function (sockets, stats, force) { if (!force && stats && (!stats.errors || stats.errors.length === 0) && stats.assets && stats.assets.every(asset => !asset.emitted) ) { return this.sockWrite(sockets, "still-ok"); } // 調用 sockWrite 方法將 hash 值通過 websocket 發送到瀏覽器端 this.sockWrite(sockets, "hash", stats.hash); if (stats.errors.length > 0) { this.sockWrite(sockets, "errors", stats.errors); } else if (stats.warnings.length > 0) { this.sockWrite(sockets, "warnings", stats.warnings); } else { this.sockWrite(sockets, "ok"); } };第三步:webpack-dev-server/client 接收到服務端消息做出響應
webpack-dev-server 修改了webpack 配置中的 entry 屬性,在里面添加了 webpack-dev-client 的代碼,這樣在最后的 bundle.js 文件中就會接收 websocket 消息的代碼了。
webpack-dev-server/client 當接收到 type 為 hash 消息后會將 hash 值暫存起來,當接收到 type 為 ok 的消息后對應用執行 reload 操作。
在 reload 操作中,webpack-dev-server/client 會根據 hot 配置決定是刷新瀏覽器還是對代碼進行熱更新(HMR)。代碼如下:
// webpack-dev-server/client/index.js hash: function msgHash(hash) { currentHash = hash; }, ok: function msgOk() { // ... reloadApp(); }, // ... function reloadApp() { // ... if (hot) { log.info("[WDS] App hot update..."); const hotEmitter = require("webpack/hot/emitter"); hotEmitter.emit("webpackHotUpdate", currentHash); // ... } else { log.info("[WDS] App updated. Reloading..."); self.location.reload(); } }第四步:webpack 接收到最新 hash 值驗證并請求模塊代碼
首先 webpack/hot/dev-server(以下簡稱 dev-server) 監聽第三步 webpack-dev-server/client 發送的 webpackHotUpdate 消息,調用 webpack/lib/HotModuleReplacement.runtime(簡稱 HMR runtime)中的 check 方法,檢測是否有新的更新。
在 check 過程中會利用 webpack/lib/JsonpMainTemplate.runtime(簡稱 jsonp runtime)中的兩個方法 hotDownloadManifest 和 hotDownloadUpdateChunk。
hotDownloadManifest 是調用 AJAX 向服務端請求是否有更新的文件,如果有將發更新的文件列表返回瀏覽器端。該方法返回的是最新的 hash 值。
hotDownloadUpdateChunk 是通過 jsonp 請求最新的模塊代碼,然后將代碼返回給 HMR runtime,HMR runtime 會根據返回的新模塊代碼做進一步處理,可能是刷新頁面,也可能是對模塊進行熱更新。該 方法返回的就是最新 hash 值對應的代碼塊。
最后將新的代碼塊返回給 HMR runtime,進行模塊熱更新。
附:為什么更新模塊的代碼不直接在第三步通過 websocket 發送到瀏覽器端,而是通過 jsonp 來獲取呢?
我的理解是,功能塊的解耦,各個模塊各司其職,dev-server/client 只負責消息的傳遞而不負責新模塊的獲取,而這些工作應該有 HMR runtime 來完成,HMR runtime 才應該是獲取新代碼的地方。再就是因為不使用 webpack-dev-server 的前提,使用 webpack-hot-middleware 和 webpack 配合也可以完成模塊熱更新流程,在使用 webpack-hot-middleware 中有件有意思的事,它沒有使用 websocket,而是使用的 EventSource。綜上所述,HMR 的工作流中,不應該把新模塊代碼放在 websocket 消息中。
第五步:HotModuleReplacement.runtime 對模塊進行熱更新這一步是整個模塊熱更新(HMR)的關鍵步驟,而且模塊熱更新都是發生在HMR runtime 中的 hotApply 方法中
// webpack/lib/HotModuleReplacement.runtime function hotApply() { // ... var idx; var queue = outdatedModules.slice(); while(queue.length > 0) { moduleId = queue.pop(); module = installedModules[moduleId]; // ... // remove module from cache delete installedModules[moduleId]; // when disposing there is no need to call dispose handler delete outdatedDependencies[moduleId]; // remove "parents" references from all children for(j = 0; j < module.children.length; j++) { var child = installedModules[module.children[j]]; if(!child) continue; idx = child.parents.indexOf(moduleId); if(idx >= 0) { child.parents.splice(idx, 1); } } } // ... // insert new code for(moduleId in appliedUpdate) { if(Object.prototype.hasOwnProperty.call(appliedUpdate, moduleId)) { modules[moduleId] = appliedUpdate[moduleId]; } } // ... }
模塊熱更新的錯誤處理,如果在熱更新過程中出現錯誤,熱更新將回退到刷新瀏覽器,這部分代碼在 dev-server 代碼中,簡要代碼如下:
module.hot.check(true).then(function(updatedModules) { if(!updatedModules) { return window.location.reload(); } // ... }).catch(function(err) { var status = module.hot.status(); if(["abort", "fail"].indexOf(status) >= 0) { window.location.reload(); } });第六步:業務代碼需要做些什么?
當用新的模塊代碼替換老的模塊后,但是我們的業務代碼并不能知道代碼已經發生變化,也就是說,當 hello.js 文件修改后,我們需要在 index.js 文件中調用 HMR 的 accept 方法,添加模塊更新后的處理函數,及時將 hello 方法的返回值插入到頁面中。代碼如下
// index.js if(module.hot) { module.hot.accept("./hello.js", function() { div.innerHTML = hello() }) }更多內容在我的 Github
https://github.com/zhongmeizh...
參考:餓了么前端
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/109871.html
摘要:原理踩坑起因最近在做框架的熱更新,記錄一下的原理和小坑。文件系統接收更改并通知。運行時通過請求這些更新。類似的問題還有很多,事件綁定手動插入并且沒有銷毀的定時器等,記得把這些副作用一起干掉。參考官方文檔原理分析與實現 webpack hot-module-replacement 原理&踩坑 起因 最近在做san框架的熱更新,記錄一下webpack HMR的原理和小坑。 什么是HMR? ...
摘要:應用源碼分析解讀結論熱更新的流程在構建項目時會創建服務端基于和客戶端通常指瀏覽器,項目正式啟動運行時雙方會通過保持連接,用來滿足前后端實時通訊。服務端源碼的關鍵部分每一個都是沒有屬性,表示沒有發生變化。 webpack-dev-server 簡介 Use webpack with a development server that provides live reloading. Th...
摘要:源碼解析起因最近在搞框架的熱加載方案,自然是少不了向成熟的框架學習偷窺。這將銷毀并重建整個組件包括子組件。通過使用說明可以看出,暴露的接口還是很清晰的,下面來看下具體源碼實現。 Vue-hot-reload-api 源碼解析 起因 最近在搞san框架的熱加載方案,自然是少不了向成熟的框架學習(偷窺ing)。熱加載方案基本也只是主流框架在做,且做的比較成熟,大部分應用開發者并不會接觸到這...
摘要:如果檢測到文件變化,會重新構建被改變的文件。另外,被改變的模塊被發送到,用來做熱替換。首先檢查,被更新的模塊能否指是否被跟蹤詢問實例是否有更新。如果有更新,實例會異步下載更新代碼,并通知已經準備就緒。參考資料官方文檔官方同事的總結 Hot Module Replacement是webpack下實現熱刷新的模塊,由于webpack的坑爹文檔,看了很久才搞明白這東西怎么用。 showImg...
閱讀 1611·2021-11-23 09:51
閱讀 1181·2019-08-30 13:57
閱讀 2261·2019-08-29 13:12
閱讀 2016·2019-08-26 13:57
閱讀 1198·2019-08-26 11:32
閱讀 981·2019-08-23 15:08
閱讀 706·2019-08-23 14:42
閱讀 3085·2019-08-23 11:41