webpack的热更新原理

Webpack热更新( Hot Module Replacement,简称 HMR),无需完全刷新整个页面的同时,更新所有类型的模块,是 Webpack 提供的最有用的功能之一。

  • 保留在完全重新加载页面期间丢失的应用程序状态。
  • 只更新变更内容,以节省宝贵的开发时间。
  • 在源代码中对 CSS / JS 进行修改,会立刻在浏览器中进行更新,这几乎相当于在浏览器 devtools 直接更改样式。
    HMR作为一个Webpack内置的功能,可以通过HotModuleReplacementPlugin--hot开启。

刷新我们一般分为两种:

  • 一种是页面刷新,不保留页面状态,就是简单粗暴,直接window.location.reload()
  • 另一种是基于WDS (Webpack-dev-server)的模块热替换,只需要局部刷新页面上发生变化的模块,同时可以保留当前的页面状态,比如复选框的选中状态、输入框的输入等。

webpack 构建

项目启动之后,会进行首次构建打包,控制台中会输出整个的构建过程,可以观察到一个 Hash 值 3606e1ab1ddcf6626797。

在每次代码的修改后,保存时都会在控制台上出现 compiling...字样,可以在控制台中观察到:

  • Hash 值更新:4f8c0eff7ac051c13277;
  • 新生成文件:3606e1ab1ddcf6626797.hot-update.json
  • main1.3606e1ab1ddcf6626797.hot-update.js。

    如果没有任何改动,直接保存,控制台输出编译打包信息,Hash 值没有发生变化,仍旧是 4f8c0eff7ac051c13277。

    再次修改代码保存,控制台输出编译打包信息。根据文件名可以发现,上次输出的 Hash 值被作为本次编译新生成的 Hmr 文件标识。同样,本次输出的 Hash 值会被作为下次热更新的标识。

webpack watch

为什么代码的改动保存会自动编译,重新打包?这一系列的重新检测编译依赖于 Webpack 的文件监听:在项目启动之后,Webpack 会通过 Compiler 类的 Run 方法开启编译构建过程,编译完成后,调用 Watch 方法监听文件变更,当文件发生变化,重新编译,编译完成之后继续监听。

页面的访问需要依赖 Web 服务器,那要如何将 Webpack 编译打包之后的文件传递给 Web 服务器呢?这就要看 Webpack-dev-middleware了。 Webpack-dev-middleware 是一个封装器( wrapper ),它可以把 Webpack 处理过的文件发送到一个Server(其中 Webpack-Dev-Server 就是内置了 Webpack-dev-middlewareExpress 服务器)。上面有说到编译之后的文件会被写入到内存,而 Webpack-dev-middleware 插件通过 memory-fs 实现静态资源请求直接访问内存文件。

js 复制代码
	const webpack = require('webpack');
	const webpackConfig = require('./webpack.dev.conf');
	const compiler = webpack(webpackConfig);
	debug('webpack编译完成');
	debug('将编译流通过webpack-dev-middleware');
	const devMiddleware = require('webpack-dev-middleware')(compiler, {
	// self-define options
	});

上面代码可以看到,Webpack 编译打包之后得到一个 Compilation ,并将 Compilation 传递到 Webpack-dev-middleware 插件中,Webpack-dev-middleware 可以通过 Compilation 调用 Webpack中 的 Watch 方法实时监控文件变化,并重新编译打包写入内存。

留意一下浏览器端,在 Network 中可以看到几个请求:

/__Webpack_hmr 请求返回的消息包含了首次 Hash 值,每次代码变动重新编译后,浏览器会发出 hash.hot-update.json、fileChunk.hash.hot-update.js 资源请求。

点开查看 hash.hot-update.json 请求,返回的结果中,h 是一个 hash 值,用于下次文件热更新请求的前缀,c 表示当前要热更新的文件是 main1 。

继续查看 fileChunk.hash.hot-update.js,返回的内容是使用 webpackHotUpdate 标识的 fileChunk 内容。

热更新源码

我们根据webpack-dev-serverpackage.json中的bin命令,可以找到命令的入口文件bin/webpack-dev-server.js

js 复制代码
	// node_modules/webpack-dev-server/bin/webpack-dev-server.js
	
	// 生成webpack编译主引擎 compiler
	let compiler = webpack(config);
	
	// 启动本地服务
	let server = new Server(compiler, options, log);
	server.listen(options.port, options.host, (err) => {
	    if (err) {throw err};
	});

本地服务代码:

js 复制代码
	// node_modules/webpack-dev-server/lib/Server.js
	class Server {
	    constructor() {
	        this.setupApp();
	        this.createServer();
	    }
	    
	    setupApp() {
	        // 依赖了express
	    	this.app = new express();
	    }
	    
	    createServer() {
	        this.listeningApp = http.createServer(this.app);
	    }
	    listen(port, hostname, fn) {
	        return this.listeningApp.listen(port, hostname, (err) => {
	            // 启动express服务后,启动websocket服务
	            this.createSocketServer();
	        }
	    }                                   
	}
总结:

这一小节代码主要做了三件事:

  • 启动webpack,生成compiler实例。compiler上有很多方法,比如可以启动 webpack 所有编译工作,以及监听本地文件的变化。
  • 使用express框架启动本地server`,让浏览器可以请求本地的静态资源。
  • 本地server启动之后,再去启动websocket服务,通过websocket,可以建立本地服务和浏览器的双向通信。这样就可以实现当本地文件发生变化,立马告知浏览器可以热更新代码啦!

如果不了解websocket,建议简单了解一下websocket速成.

参考文章

相关推荐
崔庆才丨静觅12 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606113 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了13 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅13 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅13 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅14 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment14 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅14 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊14 小时前
jwt介绍
前端
爱敲代码的小鱼14 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax