Vue 3.6 RC 把 Vapor 的事件委托改成显式 opt-in:迁移步骤与排查清单
Vue core CHANGELOG(minor 分支)在 3.6.0-rc.13(2026-10-23)里记录了一条 breaking change:Vapor 模式的事件委托从默认行为改成需要显式开启。
变更内容
DOM event listeners are now attached directly to elements by default. Previously, supported events were automatically delegated to document, which could produce behavioral differences from VDOM Mode—for example, an ancestor calling stopPropagation() could prevent a child handler from running.
也就是说,以前受支持的静态事件会被自动委托到 document,这跟 VDOM 模式不一致。典型症状是:祖先元素调用了 stopPropagation(),子组件上的 handler 根本不执行——因为监听器挂在 document 上,事件在冒泡途中就被截断了。
现在默认把监听器直接挂在元素本身,与标准 Vue 和原生 DOM 的行为对齐。需要委托时,用 Vapor 专有的 .delegate 修饰符显式开启,仅对受支持的静态事件有效:
同时,compilerOptions.eventDelegation 选项已被移除。配置文件里留着它不会再生效,还可能直接报错。
迁移步骤
- 全局搜索
eventDelegation,从vue.config.js、vite.config.ts或编译器配置里删掉该选项。 - 找出真正依赖 document 级委托的监听器,逐个加上
.delegate:
- 确认目标事件是受支持的静态事件。动态事件名(
@[eventName])无法附加修饰符,这部分只能接受直挂语义。
排查清单
- 长列表 / 表格:大量行内监听器以前被合并到
document上一个,现在每个元素各挂一个,检查监听器数量与内存占用是否明显上升。 - 动态生成的节点:如果代码假设“节点后插入也能被委托捕获”,要重新验证,直挂模式下监听器必须随节点一起创建。
- 依赖旧冒泡行为的代码:重点看是否存在“祖先
stopPropagation()之后子 handler 仍被触发”的隐式依赖。这类逻辑在默认直挂后会失效,需要显式加.delegate或调整事件层级。 - 事件被阻止后的兜底逻辑:有些弹层、下拉关闭逻辑建立在 document 级监听上,迁移后要确认点击外部关闭仍然工作。
取舍
委托的价值是减少监听器数量,在超长列表里能明显降低注册开销;但在 Vapor 下它会改变冒泡语义,与原生 DOM 产生偏差,调试成本更高。默认直挂更贴近原生行为,语义清晰,代价是大列表场景下监听器数量上升。所以现在的默认值偏向正确性,性能敏感处再手动换回委托。
版本状态
这是 RC 行为。3.6.0-rc.13 发布于 2026-10-23,Vue 3.6 仍是预发布,stable 还是 3.5.x(3.5.43,2026-09-17)。3.6 的主要卖点是 Vapor Mode,以及对 @vue/reactivity 基于 alien-signals 的重构;官方建议现阶段两种用法:在既有应用里局部使用 Vapor 跑性能敏感页面,或用 Vapor 构建小型新应用。线上项目还在 3.5.x,不要把 RC 的行为当成稳定版承诺。
同版本其他条目:runtime-core 恢复了异步加载组件默认 200ms 延迟(#15950);runtime-vapor 修了一批 transition / async setup / hydration 问题;Vapor slot 的行现在可以作为 vdom TransitionGroup 的子节点;未使用 vaporInteropPlugin 时 runtime-core 会 tree-shake vapor interop 代码,未使用 Transition / KeepAlive / Suspense 时 runtime-vapor 会 tree-shake 对应的 interop 代码。
另外,Vapor 已知的限制没变:不支持 Options API、app.config.globalProperties,getCurrentInstance() 在 Vapor 组件内返回 null;不支持逐元素 @vue:xxx 事件、v-memo、组件 ref 的 $el/$props/$attrs/$slots/$refs;纯 Vapor 应用不支持 Suspense;自定义指令接口不同(reactive getter + 可选 cleanup)。