编程 终端打印 hot updated,页面上的值没变:Vite 8.3.4 在 bundled-dev 下重算 acceptExports 边界

2026-10-21 21:05:23

终端打印 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.38.3.4
终端[vite] hot updated: child.jsno hmr boundary found for module \index.html`→page reload`
子模块回调CHILD_ACCEPT L0 E1不触发
父模块执行不重跑PARENT_EXEC L0 E1 2
DOML0 | extra=E0 | loads=1L0 | extra=E1 | loads=2

源码差异

8.3.3 的 bundledDevHmrClient.ts 里没有 getAcceptedExports。8.3.4 把「父模块实际用了哪些导出」算进边界,没算进去的导出不再被视为 accept 成功。

另外两个坑

  • 不开 hmrPartialAccept 时,普通 dev(未开 bundledDev)会直接 page reload child.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、屏幕留旧字符串」比多刷一次页更难查。

复制全文 生成海报 Vite 前端调试 HMR

推荐文章

程序员茄子在线接单