认识
一、认识
HMR
的全称叫做 Hot Module Replacement
,即模块热替换或者模块热更新。它可以在开发阶段实现局部实时更新、快速重新加载模块和状态保存, 不用刷新整个页面。相比于传统的 Live Reload
所解决的问题:模块局部更新和状态保存。
Webpack
接受更新的策略: 接受自身更新、接受依赖模块的更新和接受多个子模块的更新
二、使用
Webpack
生态下,只需要经过简单的配置,即可启动 HMR
功能,大致分两步:
-
设置
devServer.hot
属性为true
// webpack.config.js
module.exports = {
// ...
devServer: {
// 必须设置 devServer.hot = true,启动 HMR 功能
hot: true
}
}; -
之后,还需要在代码调用
module.hot.accept
接口,声明如何将模块安全地替换为最新代码,如import component from "./component";
let demoComponent = component();
document.body.appendChild(demoComponent);
// HMR interface
if (module.hot) {
// Capture hot update
module.hot.accept("./component", () => {
const nextComponent = component();
// Replace old content with the hot loaded one
document.body.replaceChild(nextComponent, demoComponent);
demoComponent = nextComponent;
});
}
三、工作
-
使用
Express
启动本地服务, 构造静态服务器: 通过webpack.config.js
创建webpack
实例, 将compiler.outputFileSystem
修改为memory-fs
的实例: 创建compiler
对象, 并修改compiler.outputFileSystem = memory-fs 实例
, 将Webpack
的产物打包到内存中, 因此读写都比较快。memory-fs
是NodeJS
原生fs
模块内存版(in-memory
)的完整功能实现; 使用Express
启动本地服务, 处理响应来自客户端的资源请求: 从内存中读取index.html
、bundle.js
、chunkId.hash.hot-update.json
、chunkId.hash.hot-update.js
文件内容返回给客户端。 -
浏览器加载页面后, 服务端和客户端建立
WebSocket
长连接: 启动一个WebSocket
服务器,Webpack
重新编译时, 向客户端推送hash
和ok
两个事件 -
Webpack
监听文件的变化, 每当文件发生变更时,Webpack
将会监控到此时文件变更事件,并找到其对应的module
, 增量构建发生变更的模块: 每次编译都会生成hash
值。通过HotModuleReplacementPlugin
插件, 对比编译后的chunk
和module
,将更新后的module
和runtime
形成新的HotUpdateChunk
, 生成记录着已改动模块的chunkId.hash.hot-update.json
文件和记录着已改动模块代码的chunkId.hash.hot-update.js
文件 -
编译完成后, 触发
compiler.hooks.done
钩子, 在回调函数中通过Websocket
发送hash
事件和ok
事件, 把变更模块通知到客户端 -
客户端通过
hash
事件接受本次编译之后的hash
值, 如果前后hash
值不一致, 则ajax
请求manifest
资源文件chunkId.hash.hot-update.json
确认增量变更module
和chunk
, 然后再通过JSONP
请求chunkId.hash.hot-update.js
获取变更module
和chunk
的代码: 之所以使用JSONP
获取变更的代码而不用ajax
或者websocket
是因为JSONP
获取的代码可以直接执行。 -
获取变更代码之后, 使用新的模块对旧模块进行热替换,并删除其缓存: 在
webpack
的运行时中, 通过__webpack__modules__
来维护所有的模块, 通过chunk
的方式加载最新的modules
,找到__webpack__modules__
中对应的模块逐一替换,并删除其上下缓存。 -
Webpack
运行时触发变更模块的module.hot.accept
回调, 执行代码变更逻辑: 一旦某个模块没有注册对应的module.hot.accept
函数后,HMR
运行时会执行兜底策略, 通常是刷新页面, 确保页面上运行的始终是最新的代码。
四、问题
4.1 HMR 与 Live Reload 有什么区别?
HMR
: 无需刷新在内存环境中即可替换掉过旧模块, 相对于 Live Reload
, 整体刷新页面方案, HMR
的优点在于可以做到模块局部更新, 可以保存应用的状态, 提高开发效率。
Live Reload
: 代码进行更新后, 在浏览器自动刷新以获取最新前端代码
4.2 Webpack HMR 与 Vite HMR 有什么区别?
与webpack
的热更新对比起来,两者都是建立socket
联系,但是两者不同的是,前者是通过bundle.js