Vite 实现高效 HMR 的核心是绕过打包、依托浏览器原生 ESM,仅编译替换最小必要模块,响应控制在毫秒级;通过轻量 ModuleGraph 精准定位更新边界;客户端主动拉取新模块并 patch,保留状态;需注意静态导入、显式 accept、避免顶层副作用等实操要点。

Vite 实现高效模块热更新(HMR)的核心,是绕过打包、直接依托浏览器原生 ESM 加载机制,在变更发生时只编译和替换最小必要模块,整个过程控制在毫秒级响应。
基于原生 ESM 的按需编译与模块替换
Vite 开发服务器不预先构建整个项目,而是将每个源文件(如 .ts、.vue、.jsx)视为独立的 ESM 模块。浏览器请求时,服务端实时编译该文件并注入 HMR 能力(通过 import.meta.hot),再返回给浏览器执行。这意味着:
- 改一个组件文件,Vite 只处理它本身及其直系依赖,不解析全量 AST 或重建依赖图
- 没有 Webpack 那类“打包 → 编译 → 注入 runtime → 重新加载 chunk”的链路,省去大量中间环节
- 首次启动快,后续每次修改响应时间通常为 5–50ms
精准传播:依赖图驱动的更新边界判定
Vite 启动时会构建轻量级模块依赖图(ModuleGraph),记录每个模块的 importers(谁引用我)和 importedModules(我引用谁)。当文件变更时:
- 服务端根据该图快速定位“受影响模块”和“更新边界”
- 若修改的是纯工具函数(无副作用、仅导出),且调用方写了
import.meta.hot.accept('./utils.js', ...),则只重载该模块,父模块不执行 - 若未显式接受更新,HMR 会向上冒泡,直到遇到
accept声明或抵达入口文件(此时整页 reload)
客户端原子 patch:状态保留的关键
HMR 不是简单刷新 DOM,而是由客户端主动拉取新模块并执行 patch:
立即学习“Java免费学习笔记(深入)”;
- 服务端通过 WebSocket 主动推送变更通知(含路径、时间戳、hash)
- 客户端收到后,发起带时间戳参数的 GET 请求(如
/src/components/Button.vue?t=1724145240123),强制绕过缓存获取最新模块 - 框架插件接管后续逻辑:Vue 卸载旧实例、挂载新组件;React Fast Refresh 保留 state 并替换函数体;CSS 更新则直接替换
<style>标签内容
保障 HMR 稳定生效的实操要点
很多 HMR 失效问题其实源于配置或写法破坏了依赖追踪链路:
- 动态导入必须用静态字符串:
import('./pages/Home.vue')✅,import(`./pages/${name}.vue`)❌ - 入口文件建议显式启用热重载,例如 Vue 项目中在
main.js添加import.meta.hot?.accept(() => { app.unmount(); createApp(App).mount('#app') }) - 避免在模块顶层执行副作用(如
setTimeout、document.body.append),否则热更新可能中断或状态错乱;应封装进onMounted或hot.dispose清理 - 确保
vite.config.js中server.hmr未被禁用,并正确配置别名(如@: '/src'),防止路径解析失败导致模块无法被识别


















