Vite HMR热更新失效排查:自定义HTML缺少@vite/client引用的修复方法
Vite 的 HMR(热模块替换)是提升开发效率的关键特性,但如果遇到修改代码后页面不再自动刷新,浏览器控制台也看不到 hot updated 日志,而手动刷新才能生效的情况,很可能是 HMR 客户端注入出了问题。本文记录一次深入排查的过程和解决方案。HMR 的基本流程:Vite 通过文件监听检测到改动 -> 分析依赖 -> 通过 WebSocket 向浏览器发送更新通知 -> 浏览器端的 HMR 客户端加载新模块完成替换。其中浏览器端的 @vite/client 脚本是核心组件。
问题现象:WebSocket 连接正常建立(状态码 101),甚至能看到服务端发出的更新消息(如 { type: 'update', path: '/src/main.js' }),但页面就是不自动刷新。说明服务端通知已发出,但浏览器端没有正确响应。
排查步骤:
1. 检查 vite.config.js 是否显式禁用了 HMR:
export default {
server: {
hmr: true // 默认即为true
}
}
配置无误,排除了关闭 HMR 的可能。
2. 检查 WebSocket 连接状态。在浏览器开发者工具的 Network 面板中确认连接建立,且发送消息正常。
3. 检查 HTML 文件中是否引入了 HMR 客户端脚本。如果项目使用了自定义 HTML 入口文件,而 Vite 不会自动注入 @vite/client,必须手动添加。
最终发现根因:直接在 HTML 中写了
<script type="module" src="/src/main.js"></script>
却遗漏了
<script type="module" src="/@vite/client"></script>
。没有这一行,浏览器就无法加载 HMR 客户端逻辑,自然无法响应服务端通知。
修复方法:在自定义 HTML 的 <head> 或 <body> 中,在业务脚本之前添加 @vite/client 的引用:
<!DOCTYPE html>
<html>
<head>
<script type="module" src="/@vite/client"></script>
<script type="module" src="/src/main.js"></script>
</head>
<body>
</body>
</html>
其他常见陷阱:
- base 配置不匹配:如果设置了 base: '/sub-path/',则脚本路径也需要调整为
<script type="module" src="/sub-path/@vite/client"></script>
- Proxy 或中间件干扰:自定义服务器中间件或反向代理(如 Nginx)可能拦截 WebSocket 升级请求,需确保代理配置允许 WebSocket 通信。
- Chrome 扩展冲突:部分广告拦截器会屏蔽 WebSocket 或脚本注入,可尝试在无痕模式下测试或禁用扩展。
这次经历提醒我们,Vite 的“约定优于配置”虽然简化了开发,但自定义项目结构时务必关注隐式依赖。如果 HMR 突然失效,优先检查 HTML 中是否包含了 @vite/client。
Re: Vite HMR热更新失效排查:自定义HTML缺少@vite/client引用的修复方法
感谢分享,这个排查过程很清晰。我自己也遇到过类似问题,尤其是用自定义HTML模板时容易忽略这个隐式依赖。你提到的 base 路径和代理干扰点也很关键,之前因为 Nginx 没配 WebSocket 升级踩过坑。以后 HMR 失效就按这个顺序查了,非常实用!Re: Vite HMR热更新失效排查:自定义HTML缺少@vite/client引用的修复方法
感谢楼主分享这么详细的排查过程!自定义HTML入口确实容易忽略 @vite/client 的引用,我当初也踩过这个坑。你提到的 WebSocket 连接正常但页面不刷新这个现象,跟服务端已经发消息而客户端没接收逻辑完全吻合,定位得很准。另外 base 路径和代理拦截这两个陷阱也很实用,下次遇到类似问题我会优先检查这三个地方。收藏了!Re: Vite HMR热更新失效排查:自定义HTML缺少@vite/client引用的修复方法
感谢分享!这个问题确实很容易踩坑,特别是从旧版 Vite 迁移或者自己写 HTML 模板时。我上次也遇到过类似情况,排查半天才发现是忘了加 @vite/client,后来习惯在新建 HTML 时就把这行先写上。另外你提到的 base 路径问题也很关键,很多时候配置一变就忘了调整脚本地址。总结得很清晰,收藏了!
页:
[1]