Q09:Webpack HMR 原理
⏱ 预计阅读时间:3 分钟
原题:Webpack 的 HMR(热更新)原理解析一下?修改一个文件后,浏览器是怎么做到不刷新就能更新视图的?
🎯 面试官在考什么
- 考点1:Webpack HMR 的完整通信流程(文件变更 → 浏览器更新)
- 考点2:WebSocket 长连接在 HMR 中的角色
- 考点3:增量更新(patch)与 module.hot.accept 的配合机制
✅ 答题框架(建议顺序)
- 整体流程:文件变更 → Webpack 重新编译 → 生成 patch(增量文件)→ dev server 通过 WebSocket 通知浏览器 → 浏览器拉取 patch → 替换对应模块
- WebSocket 通信细节:dev server 启动时建立 WS 长连接;文件变更后发送
hash消息(新构建 hash)和ok消息(编译成功);浏览器收到后用 JSONP 请求拉取增量 chunk - 模块替换机制:HMR Runtime 收到 patch 后,从变更模块开始向上冒泡查找
module.hot.accept的调用者——谁 accept 了就谁处理;没人 accept 则整页刷新(fallback) - 关键区分:HMR 不等于 Live Reload。Live Reload 是整页刷新丢状态,HMR 是精确替换模块保留状态
⚠️ 常见踩坑
- "HMR 就是自动刷新页面" → 那是 Live Reload。HMR 的核心价值是保留应用状态(如已填的表单、组件内部状态),不整页刷新
- "只要改了代码浏览器就自动更新" → 需要
module.hot.accept配合。如果变更模块和它的所有父模块都没有 accept,HMR 会降级为整页刷新
💎 加分项
- 提到 HMR 的 patch 文件命名规则:
[hash].hot-update.json(记录哪些模块变更)+[id].[hash].hot-update.js(增量代码),用 JSONP 拉取而非 XHR - 提到
module.hot.accept的两种用法:自接受(当前模块自己处理更新)和父接受(父模块处理子模块更新),Vue/React 的 HMR loader 通常帮你自动注入了 accept - 提到 Webpack 5 的持久化缓存 + HMR 配合:缓存让重编译更快,HMR 通信流程不变,但整体热更新延迟更低
📚 关键知识点速查
- HMR Runtime:注入到浏览器端的运行时,负责接收 WebSocket 消息、拉取 patch、执行模块替换
- WebSocket:dev server 与浏览器间的长连接通道,用于实时推送编译状态和更新 hash
- patch(增量更新):Webpack 只重新编译变更模块及其依赖,生成增量 chunk 文件,而非全量重打包
- module.hot.accept:开发者声明"我能处理某个模块的更新",是 HMR 精确替换的关键 API
- 冒泡机制:变更模块如果自己没有 accept,HMR 会沿依赖树向上冒泡,直到找到 accept 的父模块;找不到则 fallback 到整页刷新
- Live Reload vs HMR:Live Reload = 整页刷新丢状态;HMR = 模块级替换保状态
优先级:🟡中频
🔗 推荐阅读
- Webpack 的热更新原理(掘金·錦鱼·原创) — HMR 完整流程:文件变更→编译→WS 通知→patch 拉取→模块替换
- Webpack 热更新(HMR)实现原理(腾讯云·伯爵·原创) — 2020 年经典好文,服务端+客户端双视角,增量 patch 机制详解
- Webpack HMR 官方文档 — module.hot.accept 用法、冒泡机制、配置方式
- 看完这篇面试再也不怕被问 Webpack 热更新(掘金·原创) — 流程图+源码级拆解,WebSocket→JSONP→模块替换全链路