HMR 原理
1. 面试题
Q: Vite 的 HMR 是如何实现的?和 Webpack 的 HMR 相比有什么不同?
2. 核心解答
Vite 的 HMR 与 Webpack 大同小异,也是基于 WebSocket 通信,但它更贴近 浏览器原生 ESM 的特性。
(1) HMR 流程
- Chokidar 监听文件变化:Vite 在 Node 服务端监听文件修改 (
watchChange)。
- 模块图更新:Vite 维护了一个依赖模块图,定位到变更的模块。
- 发送 WebSocket 消息:如果变更的模块支持 HMR (接受了 update),Vite 会向客户端发送
{ type: 'update', updates: [...] } 消息。
- 客户端更新:
- 客户端收到消息后,根据变更的路径 (path) 和时间戳 (timestamp),请求最新的文件内容。
- 浏览器发起新的 HTTP 请求:
/src/button.vue?t=1678888888。
- import.meta.hot.accept 回调执行,重新执行该模块,并替换旧模块。
- 如果上层模块没有 accept,则整页刷新 (fallback to full reload)。
(2) 与 Webpack HMR 的区别
- Webpack: HMR 更新时,需要重新构建整个 Bundle 中的 chunk,生成 hash,通知浏览器获取 jsonp 更新包。随着项目越大,构建时间越长,HMR 延迟越高。
- Vite: HMR 更新时,只需要编译这一个文件。浏览器直接请求该文件。无论项目多大,HMR 速度都是毫秒级的 (O(1) 复杂度)。
3. 面试加分项
Q: 为什么 Vue/React 不需要手动写import.meta.hot?
因为 Vite 插件 (例如 @vitejs/plugin-react) 自动在代码编译时注入了 HMR 代码。这就是为什么修改组件代码时页面不会刷新,只会更新该组件。
4. 总结
- Native ESM: Browser request updated module directly.
- WebSocket: Notify update path & timestamp.
- Speed: O(1) complexity, independent of project size.
- Plugin: Auto inject HMR code for frameworks.