1、HMR
全稱 Hot Module Replacement,可以理解為模塊熱替換,指在應用程序運行過程中,替換、添加、刪除模塊,而無需重新刷新整個應用
例如,我們在應用運行過程中修改了某個模塊,通過自動刷新會導致整個應用的整體刷新,那頁面中的狀態信息都會丟失
如果使用的是 HMR,就可以實現只將修改的模塊實時替換至應用中,不必完全刷新整個應用
const webpack = require("webpack");
module.exports = {
// ...
devServer: {
// 開啟 HMR 特性
hot: true,
// hotOnly: true
},
};
通過上述這種配置,如果我們修改並保存 css 文件,確實能夠以不刷新的形式更新到頁面中
但是,當我們修改並保存 js 文件之后,頁面依舊自動刷新了,這里並沒有觸發熱模塊
所以,HMR 並不像 Webpack 的其他特性一樣可以開箱即用,需要有一些額外的操作
我們需要去指定哪些模塊發生更新時進行 HRM,如下代碼:
在 webpack 中配置開啟熱模塊也非常的簡單,如下代碼:
if (module.hot) {
module.hot.accept("./util.js", () => {
console.log("util.js更新了");
});
}
2、原理
首先來看看一張圖,如下:
-
Webpack Compile:將 JS 源代碼編譯成 bundle.js
-
HMR Server:用來將熱更新的文件輸出給 HMR Runtime
-
Bundle Server:靜態資源文件服務器,提供文件訪問路徑
-
HMR Runtime:socket 服務器,會被注入到瀏覽器,更新文件的變化
-
bundle.js:構建輸出的文件
-
在 HMR Runtime 和 HMR Server 之間建立 websocket,即圖上 4 號線,用於實時更新文件變化
上面圖中,可以分成兩個階段:
- 啟動階段為上圖 1 - 2 - A - B
在編寫未經過 webpack 打包的源代碼后,Webpack Compile 將源代碼和 HMR Runtime 一起編譯成 bundle 文件,傳輸給 Bundle Server 靜態資源服務器
- 更新階段為上圖 1 - 2 - 3 - 4
當某一個文件或者模塊發生變化時,webpack 監聽到文件變化對文件重新編譯打包,編譯生成唯一的 hash 值,這個 hash 值用來作為下一次熱更新的標識
根據變化的內容生成兩個補丁文件:manifest(包含了 hash 和 chundId,用來說明變化的內容)和 chunk.js 模塊
由於 socket 服務器在 HMR Runtime 和 HMR Server 之間建立 websocket 鏈接,當文件發生改動的時候,服務端會向瀏覽器推送一條消息,消息包含文件改動后生成的 hash 值,如下圖的 h 屬性,作為下一次熱更新的標識
在瀏覽器接受到這條消息之前,瀏覽器已經在上一次 socket 消息中已經記住了此時的 hash 標識,這時候我們會創建一個 ajax 去服務端請求獲取到變化內容的 manifest 文件
mainfest 文件包含重新 build 生成的 hash 值,以及變化的模塊,對應上圖的 c 屬性
瀏覽器根據 manifest 文件獲取模塊變化的內容,從而觸發 render 流程,實現局部模塊更新
3、總結
關於 webpack 熱模塊更新的總結如下:
-
通過 webpack-dev-server 創建兩個服務器:提供靜態資源的服務(express)和 Socket 服務
-
express server 負責直接提供靜態資源的服務(打包后的資源直接被瀏覽器請求和解析)
-
socket server 是一個 websocket 的長連接,雙方可以通信
-
當 socket server 監聽到對應的模塊發生變化時,會生成兩個文件.json(manifest 文件)和.js 文件(update chunk)
-
通過長連接,socket server 可以直接將這兩個文件主動發送給客戶端(瀏覽器)
-
瀏覽器拿到兩個新的文件后,通過 HMR runtime 機制,加載這兩個文件,並且針對修改的模塊進行更新
轉自 微信公眾號:JS 每日一題
