终端打印 hot updated,页面上的值没变:Vite 8.3.4 在 bundled-dev 下重算 acceptExports 边界
版本与背景
Vite 8.3.4(2026-10-08)的 Features 只有一条:bundled-dev 支持 import.meta.hot.acceptExports(PR #23463,commit bc0f21c)。
acceptExports 挂在实验开关 experimental.hmrPartialAccept 上,默认 false。旁边还有一个 experimental.bundledDev,源码注释写的是 highly experimental。开启后开发阶段走整包,热更新边界在浏览器里计算,不再由服务端静态算——整包之后模块在客户端,服务端手里的模块图给不出准确的导出使用关系,只能按浏览器里实际的 import 来推。8.3.3 之前,bundled-dev 下 acceptExports 的边界算不准。
实测环境:vite@8.3.3 与 8.3.4,Chrome 154.0.8037.97 无头,Node v20.19.2。
两个模块
child.js 导出 label 与 extra,只接受 label:
export const label = 'L0'
export const extra = 'E0'
if (import.meta.hot) {
import.meta.hot.acceptExports(['label'], (mod) => {
console.log('CHILD_ACCEPT', mod && mod.label, mod && mod.extra)
})
}
parent.js 两个导出都用上,并用 sessionStorage 数执行次数,用来判断父模块有没有重跑:
import { label, extra } from './child.js'
配置:
experimental: {
hmrPartialAccept: true,
bundledDev: true,
}
8.3.3:hot updated,DOM 不动
页面显示 L0 | extra=E0 | loads=1。把 extra 从 E0 改成 E1。
子模块回调打印 CHILD_ACCEPT L0 E1,终端显示 [vite] hot updated: child.js,但 DOM 仍然是 L0 | extra=E0 | loads=1。
热更新宣布成功,父模块手里的 extra 还是旧值。loads 停在 1,说明父模块根本没重跑。控制台说更新了,屏幕上留的却是旧字符串:没有报错、没有警告,只能靠盯着页面上的值发现没动。
8.3.4:承认边界没罩住
同一个改动,8.3.4 的日志:
8:21:25 AM [vite] (client) hmr update child.js
8:21:25 AM [vite] (client) bundling for page reload no hmr boundary found for module `index.html`
8:21:25 AM [vite] (client) page reload
浏览器接着打印 PARENT_EXEC L0 E1 2,DOM 变成 L0 | extra=E1 | loads=2。
它承认这次边界没罩住 extra,退回整页刷新,父模块重新执行,值才是新的。宁可多刷一次页,也不假装 accept 成功。
两个版本对照:
| 8.3.3 | 8.3.4 | |
|---|---|---|
| 终端 | [vite] hot updated: child.js | no hmr boundary found for module \index.html`→page reload` |
| 子模块回调 | CHILD_ACCEPT L0 E1 | 不触发 |
| 父模块执行 | 不重跑 | PARENT_EXEC L0 E1 2 |
| DOM | L0 | extra=E0 | loads=1 | L0 | extra=E1 | loads=2 |
源码差异
8.3.3 的 bundledDevHmrClient.ts 里没有 getAcceptedExports。8.3.4 把「父模块实际用了哪些导出」算进边界,没算进去的导出不再被视为 accept 成功。
另外两个坑
- 不开
hmrPartialAccept时,普通 dev(未开bundledDev)会直接 page reloadchild.js,acceptExports写了等于没写。 - 只 import
label、不碰extra时,8.3.3 与 8.3.4 都会热更新,父模块不重跑;但回调里拿到新值,DOM 仍是旧的。文档写了边界往上的引用方不会收到通知,新值只在回调参数里;accept与acceptExports都是如此。
第二条是本来就存在的语义,不是 8.3.4 的回归。边界之下的模块更新,边界之上的引用方不会被通知,父模块不会重跑,回调参数里的新值只活在控制台,DOM 不会自动跟着变。要让 DOM 变,得在回调里自己改,或者干脆让边界失效、走整页刷新。
结论
没开 bundledDev 的项目不是必升。开了的话,8.3.3 那种「控制台说 hot updated、屏幕留旧字符串」比多刷一次页更难查。