DEBUG.WIKI
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
腾讯云;让你的想法跑起来;轻量服务器 · 建站 / Docker / API;查看产品 →
晚安云;香港 CN2 ¥15/月

Vite 热更新后懒加载读到另一份单例

Vite 8.3.5 的自接收 HMR 边界可能让父模块保留旧实例,却被后续懒加载以新时间戳再次导入。核对模块 URL,临时停用 HMR 并完整刷新,避免全局变量掩盖双实例。

页面热更新后仍然显示原来的注册状态,打开一个懒加载面板却读到空集合,父模块的初始化日志还又出现一次。若网络请求中同一文件先后带了不同的 ?t=,应检查 HMR 边界与动态导入的关系。

Vite 8.3.5 有这种具体模块图:父模块 P 已加载,P 导入一个自行接收热更新的模块;下层文件变化被这个边界接住,P 的旧实例继续存在。稍后首次加载另一个会导入 P 的模块时,却可能用新时间戳再次创建 P。

同一物理文件,两个浏览器模块 URL

浏览器按模块 URL 区分实例。P.js 与 P.js?t=... 即使返回相同源码,也可能各自执行初始化,拥有不同的模块级 Map、注册表或连接对象。

受影响的图中,热更新失效传播会给父模块记下新 HMR 时间戳,但自接收边界并没有让浏览器替换现有 P 实例。后续导入分析再把这个时间戳加到 P 的 URL,便可能与仍存活的旧实例分开。

应记录第一次加载、一次实际下层修改、HMR 接收边界和稍后懒加载的顺序。包被安装两份、循环依赖和别名解析出两个磁盘路径也能制造双实例,不能仅凭初始化日志重复就确认是时间戳路径。

临时采用完整刷新建立一致模块图

需要先恢复可靠开发状态时,可在现有 Vite 配置中停用 HMR:

server: {
  hmr: false,
},

保存原配置,停止并按原入口重启开发服务,再完整刷新浏览器。之后修改源码也需要按开发流程重新加载页面,不能继续依赖局部热替换保留的状态。

这会牺牲热更新便利,并未修复内部传播机制;它适合先验证应用在一个完整加载的模块图中是否恢复。如果应用必须保留特定 HMR 状态,应进一步检查该边界和父模块的接收、释放契约,不能仅在某处重复调用初始化。

不要靠全局单例隐藏第二次求值

把状态迁移到 globalThis 可能让两个模块暂时读到同一个对象,却会改变页面、测试和请求之间的隔离。手动删除导入中的时间戳或给所有资源强缓存,也可能让真正更新的代码无法加载。

已有副作用应继续按 HMR 接口释放,但资源释放正确不自动修复模块身份错误。要同时确认状态共享和初始化次数,而不是只让重复日志消失。

验收要重走“初始 P 已加载、下层更新被边界接收、之后才打开懒加载模块”的顺序,检查两条入口拿到同一状态,资源只按预期创建与清理。生产构建使用另一条加载链,应沿原流程单独检查,不能从这次开发 HMR 故障外推生产必然重复。