🔥 Webpack 热更新(HMR)原理详解
📌 本文适用于 Vue、React 等使用 Webpack 的项目开发者,适配 Vue CLI / 自定义 Webpack 项目。
🎯 一、什么是 HMR?
Hot Module Replacement 是 Webpack 提供的一种机制,使得浏览器在不刷新页面的前提下,只替换发生变化的模块代码。
它能做到的事:
- 修改 JS / CSS / Vue 文件时 页面不刷新
- 保持组件状态(如表单输入、滚动位置)
- 快速查看变更结果,极大提升开发效率
🧩 二、HMR 整体流程图
源码改动↓
Webpack 重新编译生成新模块↓
dev-server 通过 WebSocket 通知浏览器↓
浏览器下载更新模块↓
HMR runtime 应用新模块(替换/销毁旧模块)
⚙️ 三、核心模块组成
1️⃣ Webpack-dev-server(或 Vite 等 dev server)
- 启动本地服务,监听源码变更
- 内置 WebSocket 服务,用于通知客户端代码变化
- 静态资源由内存提供,不写入磁盘(性能高)
2️⃣ WebSocket 通信
浏览器连接到 dev-server
的 WebSocket 地址(如 ws://localhost:8080/sockjs-node
)
当文件变动,Webpack 编译完成后,会通过 WebSocket 发送如下消息:
{"type": "update","assets": [{"id": "./src/App.vue","type": "js"}]
}
3️⃣ HMR Runtime(客户端逻辑)
Webpack 构建时注入的 HMR Runtime 在浏览器中运行,主要做:
- 接收更新消息
- 下载新的模块代码(chunk)
- 执行模块的
accept
、dispose
等钩子 - 更新页面 DOM 或组件状态
💡 四、源码级更新过程(详解)
以 Vue 项目为例(vue-loader
+ webpack-dev-server
):
- 开发者修改了
App.vue
webpack-dev-server
监听到文件变动,触发 Webpack 编译- Webpack 编译出新的模块
App.vue?vue&type=template
webpack-dev-middleware
把变更信息推送给浏览器- 浏览器 HMR 客户端接收到变更,通过 JSONP 请求拉取更新模块
- 模块执行
module.hot.accept()
回调,组件重渲染,但状态保留
🧪 五、HMR 接口示例(Node API)
if (module.hot) {module.hot.accept('./math.js', function () {console.log('math.js 模块更新了!')})module.hot.dispose(() => {console.log('清理旧模块资源')})
}
🧱 六、HMR 与普通 Live Reload 的区别
特性 | HMR | Live Reload(全刷新) |
---|---|---|
刷新页面 | ❌ 不刷新 | ✅ 整页刷新 |
状态是否丢失 | ❌ 状态保留(如表单输入) | ✅ 状态丢失 |
应用速度 | ⚡ 快速(只替换模块) | 🐢 慢(页面重新加载) |
用途 | JS/CSS/Vue 组件等模块级更新 | HTML 等非模块文件的更新 |
🧰 七、常见问题与解决方案
❓ 为什么我的项目没热更新?
- ✅ 是否使用了
webpack-dev-server
或vite
? - ✅
devServer.hot = true
是否开启? - ✅ 是否在代码中正确使用了
module.hot.accept()
? - ✅ 使用了不支持 HMR 的插件(如某些 CSS 插件)?
🔧 八、HMR 配置示例(webpack.config.js)
const webpack = require('webpack')module.exports = {mode: 'development',devServer: {hot: true, // 启用 HMRstatic: './dist',},plugins: [new webpack.HotModuleReplacementPlugin()]
}
Vue CLI、Create React App 等已默认配置好 HMR
🧠 九、在框架中的实现(Vue / React)
Vue 中的 HMR(vue-loader 实现)
// App.vue
export default {name: 'App',mounted() {console.log('App Mounted')}
}if (import.meta.hot) {import.meta.hot.accept((newModule) => {console.log('模块更新!', newModule)})
}
React 中的 HMR(react-refresh 实现)
import { hot } from 'react-hot-loader/root'
const App = () => <div>Hello</div>
export default hot(App)
✅ 十、总结
模块 | 作用 |
---|---|
webpack-dev-server | 启动服务 + WebSocket 通信 |
Webpack Runtime | 管理模块热更新逻辑,替换更新模块 |
HMR API | 提供 accept / dispose 等钩子 |
WebSocket 通道 | 通知浏览器代码更新 |
热更新的真正威力在于:快速反馈 + 状态保留 + 节省时间,在现代前端开发中是不可或缺的一部分。