60 秒回答模板

Webpack HMR 不是简单刷新页面。开发服务器监听文件变化后触发增量编译,生成本次变更的 manifest 和更新 chunk,再通过 WebSocket 通知浏览器。浏览器端 HMR runtime 收到 hash 后请求更新清单和新模块,找到变更模块并沿依赖链判断是否有模块声明 accept。能处理就替换模块并执行更新回调,保留页面状态;不能处理就回退到整页刷新。

考点 核心机制与工程取舍
难度 中高频面试题
回答目标 按定义、机制、场景讲清楚

深入解析

01

文件变化和增量编译

开发环境下 dev server 或 watch 机制监听文件系统。某个模块变化后,Webpack 只重新构建受影响的模块和相关 chunk,生成新的构建 hash。

02

更新产物

HMR 更新通常包含 manifest 和 hot update chunk。manifest 描述有哪些 chunk 有更新,hot update chunk 包含新的模块代码。

03

浏览器通知

dev server 与浏览器通过 WebSocket 保持连接。编译完成后服务端推送新 hash,浏览器端 runtime 再主动拉取更新清单和模块内容。

04

模块替换决策

runtime 先定位变更模块,再沿引用链向上查找可接受更新的边界。模块或上层模块通过 module.hot.accept 声明可处理更新,才能局部替换。

05

状态保留

HMR 的价值是尽量不丢页面状态。样式更新通常容易替换;组件状态能否保留取决于框架集成、模块边界和更新逻辑,比如 React Fast Refresh 会额外处理组件签名。

06

失败回退

如果更新模块没有 accept 边界、执行更新时报错,或依赖链无法安全替换,runtime 会让浏览器整页刷新,保证页面和代码重新一致。

易错点

  • 不要把 HMR 说成“重新打包后刷新”。它的核心是增量更新和模块级替换。
  • 不要漏掉浏览器端 runtime。真正拉取更新、判断 accept、替换模块的是客户端运行时代码。
  • 不要认为所有模块都能热替换。没有 accept 边界或执行出错时必须回退。
  • 不要把 WebSocket 说成传输完整 bundle。它主要负责通知,更新文件通常再由客户端请求。

面试官追问

HMR 和 live reload 的区别是什么?

live reload 通常是检测变化后整页刷新;HMR 尝试只替换变更模块,从而保留当前页面状态。

为什么有些改动还是会刷新页面?

因为依赖链上没有可接受更新的边界,或者更新逻辑执行失败。HMR 无法证明安全替换时只能回退刷新。

HMR 的更新是怎么到浏览器的?

通知通过 WebSocket 推送,具体更新代码通常由浏览器 runtime 根据 hash 去请求 manifest 和 hot update chunk。

CSS 为什么通常能热更新?

样式模块副作用集中,替换 style 标签或 CSS 链接相对容易,不需要重建复杂的组件状态。