HMR 原理
1. 面试题
Q: Webpack 的 HMR (Hot Module Replacement) 是怎么工作的?原理是什么?
2. 核心解答
HMR 可以在不需要刷新页面的情况下,把修改后的模块代码替换到浏览器中,并保留应用的当前状态。
(1) 工作流程
- 启动 Dev Server (WebSocket):Dev Server 与浏览器建立 WebSocket 长连接。
- 监听文件变化 (Watch Mode):当源码发生变化时,Webpack 重新编译。
- 生成 HMR Manifest (Json/Js):编译完成后,Dev Server 通过 WebSocket 告诉浏览器:"有新的 Hash 了,快去更新!"。
- 下载 HMR Update (Jsonp):浏览器端收到通知,通过 AJAX 请求最新的 Manifest (包含了哪些模块变了),然后请求对应的 Chunk 更新文件 (
.hot-update.json 和 .hot-update.js)。
- 模块替换 (HMR Runtime):HMR Runtime 拿到新的模块代码,找出依赖它的旧模块,执行这些模块的 Accept 回调。如果该模块没有 Accept 逻辑,则沿着依赖树向上冒泡,直到根节点。如果还没人处理,则触发整页刷新 (Live Reload)。
(2) 关键 API
在你的入口文件中,通常会有这样的代码:
if (module.hot) {
module.hot.accept('./App', () => {
// 收到更新后的回调
render(App);
});
}
如果没有这个 module.hot.accept,HMR 就会失败,退化成全量刷新。好在现代框架 (Vue Loader / React Refresh) 帮我们自动注入了这些代码。
(3) 实现细节
- Compiler:监听文件变化。
- Server:提供静态资源服务,并通过 WebSocket 推送 Hash。
- Client (Client.js):在浏览器端接收 WebSocket 消息。
- HMR Runtime:在浏览器端处理模块热替换逻辑。
3. 面试加分项
Q: 为什么 CSS 不需要手动配置 HMR?
因为 style-loader 内部实现了 module.hot.accept。当 CSS 变化时,它会移除旧的 <style> 标签,插入新的 <style> 标签,从而实现无刷新样式更新。
Q: HMR 和 Live Reload 有什么区别?
- Live Reload:只要文件变化,就强制刷浏览器 (F5)。所有状态 (Redux/State) 会丢失。
- HMR:只替换变更的模块,保留状态 (State Preservation)。开发体验极佳。
4. 总结
- WebSocket: Server push update signal.
- Manifest: Hash based update list via AJAX.
- Runtime: Replace module code via JSONP.
- Accept: Determine update handling logic (Vue/React handles it).