代码 Vue 3.6 的 Vapor 不再默认把事件委托到 document:祖先 stopPropagation 不再吃掉子元素的处理函数

2026-11-04 21:05:00

Vue 3.6 Vapor 迁移笔记:事件委托、slot 调用与自定义指令的三处行为差异

信源:vuejs/core 的 CHANGELOG 与 3.6 RC 发布说明(v3.6.0-rc.1 / beta.1 / rc.14)。发布列表见 vuejs/core releases。

版本状态先说清楚:3.6 自 2026-07-18 进入 RC,截至 2026-10-30 最新的预发布是 3.6.0-rc.14,3.6.0 stable 尚未发布。下面的行为以 rc 线为准,落地前建议按自己锁定的 rc 号复核一遍。

Vapor 是什么,怎么开

Vapor 是 SFC 级别的可选编译目标,100% opt-in。同一份 SFC 源码:

  • 不带标记:编译成 vnode render 函数,走原来的 VDOM 路径;
  • 带标记:编译成直接操作 DOM 并订阅响应式原语的代码,没有 diff 与 patch。

第三方 benchmark(js-framework-benchmark)里性能与 Solid / Svelte 5 同级。

开启方式三种:


// 常规写法


// setup 的简写


@vitejs/plugin-vue@6.0.8 增加了插件级强制开关 features: { vapor: true },但它无法强制「只有一个普通 块」的 `.vue` 文件,因为 Vapor SFC 只支持。

另外,@vue/reactivity 在 3.6 里基于 alien-signals 重写过,公有 API(ref / reactive / computed / watch / watchEffect / effectScope)不变,目标是减少分配与依赖图遍历开销。alien-signals 是内部实现细节,不要直接 import。

入口选择直接决定包里有没有 vDOM runtime

  • createVaporApp():建全 Vapor 树,vDOM runtime 会被 tree-shake 掉。
  • createApp().use(vaporInteropPlugin):VDOM 树里嵌 Vapor 组件,vDOM runtime 仍在包里。

只要树里存在一个 vDOM 组件,或者开了 interop,vDOM runtime 就留在包里。官方 Hello World 例子在 rc.9 实测:85.10 kB / gzip 32.18 kB → 46.18 kB / gzip 17.52 kB。

差异一:事件委托改成 opt-in,默认挂在元素上

这是最容易在迁移后悄悄改变行为的一处。rc.14 之前,Vapor 会把受支持的事件自动委托到 document,这与 VDOM 模式的行为可能不同——例如祖先元素调用 stopPropagation() 会阻止子元素上的处理函数执行。现在默认把 DOM 监听器直接挂在元素上;需要委托时用 Vapor 独有的 .delegate 修饰符:


同时 compilerOptions.eventDelegation 选项已移除,已有配置要删掉,否则是不生效的死配置。

差异二:slots.default() 会真的渲染

在 Vapor 里调 slot 就是渲染它,slots.default() 不是探测 API:它会创建 DOM 节点、注册副作用、在 hydration 时认领 SSR 标记。所以不能拿它判断「有没有传 slot」来决定是否显示 fallback,这类写法要换成显式判断(例如 $slots.default 存在性)或改结构。

已知问题:VDOM 组件内部不能用 slots.default() 渲染 vapor slot,要用 renderSlot。

差异三:自定义指令签名变了

Vapor 指令是一个普通函数,其值是一个 getter,通过 watchEffect() 读取,可选返回清理函数。也就是说旧的 mounted / updated / unmounted 钩子对象形态在这里不成立,自己发布的指令需要单独出一个 Vapor 版本。

Vapor 不支持的 API 清单

迁移前逐条对照,命中任意一条就别整页迁:

  • Options API
  • app.config.globalProperties
  • getCurrentInstance()——在 Vapor 组件里返回 null
  • @vue:xxx 元素级生命周期监听
  • v-memo
  • 组件模板 ref 不暴露 $el / $props / $attrs / $slots / $refs

Vapor-only 模式下不支持 Suspense,但可以在 VDOM 的 Suspense 里渲染 Vapor 组件。

Nuxt / SSR 的 hydration

hydration 相关修复每个 RC 都在进(多根组件、async setup、slots、fragment 锚点、Teleport / KeepAlive / Transition 组合)。具体某一版修了哪几条,以对应 RC 的 CHANGELOG 为准,需自行验证。

迁移判定

  • 新应用:可以直接用 createVaporApp。
  • 现有应用:先把性能敏感的子页面迁过去,别一次性换入口。
  • 依赖 VDOM 假设的第三方组件库:单独验证,不要假设它能直接跑在 Vapor 树里。
复制全文 生成海报 Vue 前端 Vapor 迁移

推荐文章

程序员茄子在线接单