HMR 原理

1. 面试题

Q: Vite 的 HMR 是如何实现的?和 Webpack 的 HMR 相比有什么不同?

2. 核心解答

Vite 的 HMR 与 Webpack 大同小异,也是基于 WebSocket 通信,但它更贴近 浏览器原生 ESM 的特性。

(1) HMR 流程

  1. Chokidar 监听文件变化:Vite 在 Node 服务端监听文件修改 (watchChange)。
  2. 模块图更新:Vite 维护了一个依赖模块图,定位到变更的模块。
  3. 发送 WebSocket 消息:如果变更的模块支持 HMR (接受了 update),Vite 会向客户端发送 { type: 'update', updates: [...] } 消息。
  4. 客户端更新
    • 客户端收到消息后,根据变更的路径 (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.