webpack熱更新原理:webpack熱更新是什麼

背景

傳統開發頁面,每次更新的時候需要我們手動刷新瀏覽器才會更新。自從構建工具橫空出世。我們可以通過熱更新的方式來進行更新。通稱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-dev-server的熱更新

這是一個簡單的流程圖,不涉及任何複雜邏輯,這圖中我們需要關注幾個問題

  1. 為什麼需要啟動
  2. 為什麼需要添加webpack.HotModuleReplacementPlugin
  3. webpack-dev-server怎麼知道文件發生了變化
  4. 如何實現更新

現在我們有了上面的問題,來逐個去看下

  1. 為什麼需要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
複製代碼
  1. 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 編譯成功
  1. 如何實現更新

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-hant/n/226292.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
投稿專員的頭像投稿專員
上一篇 2024-12-09 14:49
下一篇 2024-12-09 14:49

相關推薦

發表回復

登錄後才能評論