背景
傳統開發頁面,每次更新的時候需要我們手動刷新瀏覽器才會更新。自從構建工具橫空出世。我們可以通過熱更新的方式來進行更新。通稱HMR(hot module replacement),也就是模塊熱替換,當你每次需要更新代碼的時候,不在需要手動刷新即可實現效果預覽。
前置知識
探究熱更新是怎麼實現之前,我們需要知道幾樣東西
- sockjs
- webpack.HotModuleReplacementPlugin
- memory-fs
- webpack-dev-middleware
- webpack一些知識和一些底層知識,否則你估計會看不懂,並且屬於懵逼狀態,對於一些東西都給出了鏈接
- Compiler事件流
memory-fs
內存文件系統,webpack默認生成的文件會丟到內存中,並不會直接寫入硬碟
webpack.HotModuleReplacementPlugin
中文文檔
英文文檔
這個是HMR實現核心,由webpack內部所提供的一個插件,該插件會提供一些方法,用於檢查變更,並告訴你每次變更所生成的文件hash
sockjs
用於發送socket請求,通知瀏覽器進行更改
webpack-dev-middleware
webpack-dev-middleware會接收一個webpack所返回的一個compiler對象,然後調用compiler.watch監聽文件更改,
我們知道webpack熱更新的時候,並不會寫入硬碟中,這是因為默認的情況下是寫入內存的,因為內存的讀取速度比硬碟會快很多,我們可以通過devServer.writeToDisk來修改
if (options.writeToDisk) {
// 寫入硬碟
} else {
// 寫入內存,並設置內存中的路徑為當前路徑
const memoryFs = new MemoryFs()
}
複製代碼這樣我們就可以知道文件是否更改,並且把文件存在了內存中,現在在回到webpack-dev-server
HMR是怎麼實現的
這裡用webpack-dev-server進行舉例,所有熱更新原理基本類似。手寫我們實現一個簡單的webpack配置文件。
const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
devServer: {
host: 'localhost',
contentBase: path.resolve(__dirname, 'src')
port: 8080,
hot: true,
open: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
template: './src/pages/index.html',
filename: 'index.html'
})
]
}
複製代碼簡單實現

這是一個簡單的流程圖,不涉及任何複雜邏輯,這圖中我們需要關注幾個問題
- 為什麼需要啟動
- 為什麼需要添加webpack.HotModuleReplacementPlugin
- webpack-dev-server怎麼知道文件發生了變化
- 如何實現更新
現在我們有了上面的問題,來逐個去看下
- 為什麼需要webpack.HotModuleReplacementPlugin
這個是webpack官方所提供的插件,該插件提供了一些方法,當我們啟動的時候webpack會給每個文件注入moudle一個對象,這個對象裡面有個module.hot對象,具體可以參考 中文文檔 ,並且webpack每次重新構建的時候會生成一個hash,當我們把devServer.writeToDisk設置為true的時候,每次更改還會生成這2個文件。
- hot-update.js是每次需要更新的文件
- hot-update.json下次更新需要生成的hash
[id].[fullhash].hot-update.js
[runtime].[fullhash].hot-update.json
複製代碼- webpack-dev-server怎麼知道文件發生了變化
這個問題比較簡單,webpack-dev-server啟動的時候,初始化了express,並且把expres傳遞給了webpack-dev-middleware,webpack-dev-middleware也是webpack團隊出品,devServer幾乎所有api都是通過這個庫去實現的。
webpack構建的時候會返回一個compiler對象,該對象會提供一個watch方法用來監聽文件更改,而webpack-dev-middleware就是通過這種方法監聽文件更改,
webpack.HotModuleReplacementPlugin也是一樣的操作。並且每次重新構建的時候webpack-dev-server都會監聽compiler的compile、invalid、done方法,會通知socket服務用於是否展示overlay(遮罩層)。
- compile 在compilation生成之前,compilation是webpack每次重新編譯所返回的一個對象
- invalid 編譯失敗
- done 編譯成功
- 如何實現更新
webpack-dev-server在啟動的時候還會給entry添加2個文件
webpack-dev-server/clinet/index.js
webpack/hot/devServer.js // 如果設置了hotonly會注入webpack/hot/only-dev-server
複製代碼現在我們知道了每次構建所產生的hash,和產生的文件,但這時候還沒辦法熱更新。我們可以記錄每次構建所生成的hash,用於判斷和當前的hash是否匹配,從而實現熱更新,並且通知瀏覽器進行更新。
webpack-dev-server/client/index.js負責每次更新的狀態,當狀態為ok和warinings的時候會執行一個reloadApp方法,該方法會emit一個webpackHotUpdate事件,並把變更的文件hash傳遞出去,然後webpack/hot/devServer.js負責監聽這個事件,當監聽到該事件的時候,保存該文件hash,並且判斷hash是否已更新並且module.hot.status為idle的時候,執行一個check方法,check會拿到一個需要更新的模塊,如果有需要更新,則調用location.reload()方法,進行更新。
if (module.hot) {
var lastHash;
var upToDate = function upToDate() {
return lastHash.indexOf(__webpack_hash__) >= 0;
};
var log = require("./log");
var check = function check() {
module.hot
.check(true)
.then(function(updatedModules) {
if (!updatedModules) {
log("warning", "[HMR] Cannot find update. Need to do a full reload!");
log(
"warning",
"[HMR] (Probably because of restarting the webpack-dev-server)"
);
window.location.reload();
return;
}
if (!upToDate()) {
check();
}
require("./log-apply-result")(updatedModules, updatedModules);
if (upToDate()) {
log("info", "[HMR] App is up to date.");
}
})
.catch(function(err) {
var status = module.hot.status();
if (["abort", "fail"].indexOf(status) >= 0) {
log(
"warning",
"[HMR] Cannot apply update. Need to do a full reload!"
);
log("warning", "[HMR] " + log.formatError(err));
window.location.reload();
} else {
log("warning", "[HMR] Update failed: " + log.formatError(err));
}
});
};
var hotEmitter = require("./emitter");
hotEmitter.on("webpackHotUpdate", function(currentHash) {
lastHash = currentHash;
if (!upToDate() && module.hot.status() === "idle") {
log("info", "[HMR] Checking for updates on the server...");
check();
}
});
log("info", "[HMR] Waiting for update signal from WDS...");
} else {
throw new Error("[HMR] Hot Module Replacement is disabled.");
}
複製代碼現在我們已經大概了解了熱更新的過程。
原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/226292.html
微信掃一掃
支付寶掃一掃