Webpack & The Hot Module Replacement熱模塊替換原理解析
The Hot Module Replacement(HMR)俗稱熱模塊替換。主要用來當代碼產生變化后,可以在不刷新游覽器的情況下對局部代碼塊進行替換更新。這在很多情況下都很有用,例如在處理彈出框時,使用HMR可以及時的看到變化,如果用刷新游覽器的方式會回到初始頁面。
很多人使用過HMR卻不知道它是如何工作的,這里會對HMR實現原理進行解析。
關於HMR需要知道的一些事
-
HMR是Webpack的一個可選功能,如果想使用需要主動打開。
-
需要通過webpack-dev-server方式來管理webpack(另一種方式是CLI)
-
HMR只能工作在實現了HMR API的loaders里,例如:‘style-loader’,'react-hot-loader'
-
HMR只能在開發環境中使用,因為HMR會在打包的js中添加了很多額外的代碼,並且webpack-dev-server也只用於開發環境。
HMR工作原理
webpack會在打包的js中注入很多js庫來讓HMR工作,下圖展示了當一個文件發生變化是HMR是如何工作的。
圖片顏色說明:
紫色:發生改變的js或者css文件
橘色:發生變化的代碼塊說明,變化后的代碼塊內容
彩蘭色:項目代碼
綠色:webpack-dev-server相關的庫,有圖中可以發現,webpack-dev-server主要負責server端和游覽器端的通信。
藍色:webpack核心和插件庫,由圖中可以發現,server端代碼的監聽以及游覽器端新代碼的替換都是由webpack的不同模塊處理。
紅色:react-loader或者style-loader等HMR庫
執行流程:
-
當監聽到文件發生變化時,webpack 使用HotModuleReplacementPlugin生成一個mainifest(一個json結構描述了發生變化的modules列表)和update file(一個js文件包含修改后的代碼內容)
-
webpack將上述變化信息告訴webpack-dev-server
-
webpack-dev-server通過webSocket給運行在游覽器上的‘webpack-dev-server/client’(在打包時注入的js代碼)發送一條‘invalide’信息以及更新后代碼的hash值(該hash值本次不會用到,使用上一版本的hash值).
-
’webpack-dev-server/client’會將上一版本代碼的hash傳遞給“hot/dev-server”
-
‘hot/dev-server’使用JsonpRuntime向server端發送帶有上版本hash的ajax請求,server端返回一個json,該json包含要所有要更新的模塊的hash值。
-
JsonpRuntime根據返回的json值使用jsonp請求具體的代碼塊,jsonp返回的js代碼類似下面:
webpackHotUpdate(0,
{
82:
function(module, exports, __webpack_require__) {
exports = module.exports = __webpack_require__(79)();
exports.push([module.id, “input {\n background: pink;\n}”, “”])
}
})
-
代碼會調用webpackHotUpdate方法並攜帶module_id和具體修改內容。
-
HMR runtime本身並不會處理代碼修改,它會將不同文件交給對應的loader runtime處理(例如:react-hot-loader runtime 或者 style-loader runtime)
-
如果更新失敗,會回退刷新游覽器獲取最新代碼。
示例
當游覽器首次加載app時,server端會推送當前代碼版本號current_hash。
當修改style文件后,server端HotModuleReplacementPlugin會根據更新內容生成manifest和js文件,文件名根據current_hash生成,然后更新current_hash,並將新的hash值推送給游覽器端,用作下次更新。
游覽器端webpack-dev-server/client接收到新的hash值后,會將previous hash值傳遞給webpack/hot/dev-server,dev-server根據previous hash請求具體的mainifest和js代碼,並使用jsonp更新。
參考文檔: