Vue 3.6 Vapor 下 slots.default() 会真的渲染插槽:判空写法与兼容性边界
来源:vuejs/core 3.6.0-rc.1 及后续 RC 的 minor 分支 CHANGELOG,仓库 vuejs/core。
在虚拟 DOM 里养成的判空习惯
在虚拟 DOM 渲染路径下,判断父组件有没有传 default 插槽是很常见的写法:
const slots = useSlots()
const hasDefault = !!slots.default
模板里也常见 v-if="$slots.default"。在虚拟 DOM 语义下,$slots.default 是一个返回 VNode 数组的函数,调用它不会立刻产生真实 DOM。因此在虚拟 DOM 里拿它判空,代价基本可以忽略。
Vapor 改变了这个前提。slots.default() 并不是一个没有副作用的只读探测 API:调用它会执行插槽自身的渲染逻辑。插槽渲染会在调用时创建 Block、生成 DOM 节点、注册响应式副作用;在 hydration 阶段,它还会去认领(claim)已经由 SSR 输出到页面上的节点。
组件里只要因为“先看一眼插槽里有没有东西”就调用它,触发这些副作用就不可避免。
官方给出的错误示范
RC 说明里直接给了反例:用 useSlots() 取到插槽内容,再用 !content 决定是否显示 fallback。这相当于把插槽完整渲染了一遍,然后把结果丢掉,只为换一个布尔值。
import { useSlots } from 'vue'
const slots = useSlots()
const content = slots.default?.()
const showFallback = !content
Fallback
这里的 content 是插槽执行后的产物,并非轻量的 VNode 描述。创建出来的 Block 和 DOM 节点不会因为“结果没被使用”而自动回收,注册的副作用也会继续留在那里。
副作用的具体表现
重复渲染。 同一段插槽逻辑被调用两次,就会被执行两次。比如在 setup 里调用一次得到 content,随后在模板里 `` 又渲染一次,插槽里的事件监听、watch、组件实例都会多出一份。
hydration 错位。 在 SSR + hydration 场景下,服务端已经输出了一份插槽对应的 DOM。客户端在判空时调用 slots.default(),会把这份已有节点当成客户端渲染要认领的目标;真正的渲染再走一遍时,节点归属和插入位置就可能对不上,表现为内容重复、位置偏移或 hydration 警告。
正确写法:让模板决定 fallback
默认内容交给模板,不在 script 里预测:
Fallback
`` 自带默认内容的能力:父组件没传插槽时自然显示 fallback,传了就渲染传入内容。整个判断在编译和运行时内部完成,不需要在 JavaScript 里提前调用插槽。
如果确实需要“有没有插槽”这个信息
判断插槽是否被传入,应使用 $slots 的键存在性判断,而不是调用插槽函数:
import { useSlots } from 'vue'
const slots = useSlots()
const hasDefault = 'default' in slots
这一条得到的信息是“父组件有没有传入 default 插槽”,并不等于“插槽渲染出来是否有内容”:传了一个渲染结果为空的插槽,键仍然存在。
另外,useSlots() / $slots 在 Vapor 组件里的确切可用性,应以官方文档为准,不要当成稳定保证来设计组件 API。
Vapor 下其他容易踩到的边界
启用 vapor 后,编译器只支持现有 Vue API 的一个子集,以下能力在 Vapor 组件里不可用:
- Options API
app.config.globalPropertiesgetCurrentInstance():在 Vapor 组件里返回null@vue:xxx逐元素生命周期事件v-memo- 组件模板 ref 不暴露
$el、$props、$attrs、$slots、$refs等属性
依赖 VNode 或组件公共实例代理的功能,在 Vapor 组件里都用不了。把既有组件迁到 Vapor 时,这几条比性能收益更值得先确认:尤其是用 getCurrentInstance() 做库内部逻辑的代码,以及通过模板 ref 读取 $el 去操作真实 DOM 的代码。
Vapor 是什么、当前状态
Vapor 是 SFC 的可选编译模式,目标是减小基础包体积、提升运行时性能。它 100% opt-in,按需开启,只覆盖现有 Vue API 的一个子集,行为在大部分场景下保持一致。
版本现状:3.6.0-rc.14 于 2026-10-30 发布,仍是预发布;稳定线 3.5.45 于 2026-11-05 发布。试 Vapor 走 3.6 的 RC,生产项目仍按 3.5.x 的稳定版本规划。