Vue 3.6 Vapor 默认不再做事件委托:监听器直接绑元素,要委托得写 .delegate
Vue 3.6 目前处于 RC 阶段(截至 2026-09-30 为 rc.10),Vapor Mode 已 feature-complete。变更来源见 v3.6.0-rc.1 release,升级过程里的其他细节可参考 Vue 3.6 RC upgrade guide。
默认行为变了:从 document 委托到直连
从 3.6.0-rc.2 起,Vapor 默认把 DOM 监听器直接绑到元素本身,不再走 document 级事件委托。此前 Vapor 的实现就是 document 级委托。
想显式启用 document 级委托,需要加 Vapor 专用的 .delegate 修饰符:
直接绑定
document 级委托
为什么把默认值换成直连
document 级委托的收益是监听器数量:不管页面上有多少元素共用同一类事件,document 上只挂一个监听器,事件冒泡上来后按 event.target 向上匹配注册项。元素少、生命周期长的时候这很划算。
问题出在元素数量大、挂载卸载频繁的场景。每次挂载和卸载都要在 document 的注册表里增删映射;事件真正触发时,还要从目标节点往上走一遍匹配。列表长、节点反复进出的时候,这部分开销会摊到每次交互上。
直连把监听器留在元素自己身上,随元素一起销毁,挂载卸载的成本更低,事件派发路径也更短,行为和原生 addEventListener 一致,跟第三方库或 Shadow DOM 打交道时少一层中间逻辑。
代价是监听器数量会随共享同一 handler 的元素数线性增长,这一点在超长列表里需要留意。
对既有代码的影响
默认行为改了,但模板写法没变,绝大多数代码不需要动。需要检查的是那些把 Vapor 的 document 委托当成既定前提的代码——如果你之前默认「事件最终是在 document 上被处理的」,现在要按直连语义重新过一遍。
性能上没有一刀切的结论:元素数量少、每个监听器都长驻,直连基本没有额外成本;元素成千上万且频繁挂卸,直连反而更稳定;反过来,如果只是一批静态列表共用同一个处理函数、又希望控制监听器数量,.delegate 仍然可用。
选法可以简化成一句:节点生命周期短、增删频繁,用默认直连;节点长期存在、数量大且 handler 复用,考虑 @click.delegate。
Vapor 的其余边界
按 SFC 逐个 opt in,三种写法之一即可:
// 或者 ,或者
- Options API 在 Vapor 中不受支持。
- 依赖 VNode 的 API,以及依赖组件公共实例代理的 API,在 Vapor 组件里不可用。
v-memo与组件模板 ref 属性列在 3.6 RC 的 unsupported list 中。- 用 render 函数或 JSX 编写的组件仍然是 VDOM 组件。
- 纯 Vapor 应用使用
createVaporApp(),不会把 Virtual DOM 运行时拉进来,基础包体积会明显下降。 - 在
createApp()创建的 VDOM 应用里使用 Vapor 组件,需要安装vaporInteropPlugin;Vapor 应用也可以安装它,以便在内部使用 VDOM 组件——但装上之后 VDOM 运行时又被带回来了,体积收益被抵消。 - 纯 Vapor 模式不支持 Suspense,不过 Vapor 组件可以渲染在 VDOM 的 Suspense 内部。
reactivity 的重写
3.6 基于 alien-signals 对 @vue/reactivity 做了一次大幅重构,性能和内存都有改善,benchmark 显示内存约减少 14%。