
Vue 3.6 现已进入公测(beta)阶段。在此版本中,我们已完成路线图规划的 Vapor 模式所有核心功能开发,该模式目前已实现与虚拟 DOM(Virtual DOM)模式下稳定功能的功能对等兼容性。请注意,纯 Vapor 模式目前暂不支持 Suspense 特性,但你仍然可以在 VDOM 环境的 Suspense 中嵌套渲染 Vapor 组件。
此外,3.6 版本基于 alien-signals 对 @vue/reactivity 包进行了重要重构。这一改动显著提升了响应式系统的运行性能,并有效降低了内存占用。
部分更新日志:
- 新功能 runtime-vapor:支持在
createDynamicComponent 中渲染代码块(render block) (#14213) (ddc1bae)
- 性能优化 runtime-vapor:实现动态 props/插槽数据源缓存 (#14208) (1428c06)
关于 Vapor 模式
Vapor 模式是 Vue 单文件组件(SFC)的一种全新编译模式,其核心目标是降低应用的基准包体积并提升运行时性能。该模式为 100% 可选启用,支持现有 Vue API 的一个子集,并且绝大部分功能的行为表现与原 API 保持一致。
根据第三方基准测试结果,Vapor 模式的性能表现已与 Solid 和 Svelte 5 等现代框架处于同一水平。
通用稳定性说明
Vue 3.6 公测版中的 Vapor 模式已完成功能开发,但仍被标记为不稳定状态。我们建议在以下场景中谨慎尝试:
- 在现有项目中局部使用(例如,将性能敏感的子页面或组件用 Vapor 模式实现);
- 基于 Vapor 模式构建全新的小型应用。
启用 Vapor 模式
Vapor 模式仅适用于使用 <script setup> 语法的单文件组件。启用方式非常简单,只需在 <script setup> 标签上添加 vapor 属性即可:
<script setup vapor>
// 你的组件逻辑
</script>
启用 Vapor 模式的组件可以在以下两种场景中使用:
场景 1:Vapor 应用实例中
通过 createVaporApp 创建的应用实例不会引入虚拟 DOM 运行时代码,因此能最大程度地减少基准包体积。
场景 2:VDOM 应用实例中
如果要在通过 createApp 创建的 VDOM 应用实例中使用 Vapor 组件,必须安装 vaporInteropPlugin 插件来实现互操作:
import { createApp, vaporInteropPlugin } from 'vue'
import App from './App.vue'
createApp(App)
.use(vaporInteropPlugin) // 启用 vapor 互操作
.mount('#app')
需要注意的是,Vapor 应用实例也可以安装此插件以支持在其中使用 VDOM 组件,但这会重新引入 VDOM 运行时,从而抵消包体积优化的收益。
VDOM 互操作限制
安装互操作插件后,Vapor 组件与非 Vapor 组件可以互相嵌套使用。目前,该特性已支持标准的 props、事件和插槽用法,但尚未覆盖所有边界情况。例如,在 Vapor 模式中使用基于 VDOM 开发的第三方组件库时,仍可能遇到兼容性问题。
一个已知问题是:在 VDOM 组件中无法通过 slots.default() 直接渲染 Vapor 插槽,必须改用 renderSlot 方法(具体示例请参考官方文档)。
我们会持续优化这一问题。但总体建议是:在应用中划分清晰的「模式区域」,尽量避免两种模式的组件深度混合嵌套。未来,我们可能会提供配套工具,用于在代码库中强制管理 Vapor 模式的使用边界。
功能兼容性
Vapor 模式在设计上仅支持现有 Vue 功能的一个子集。对于受支持的功能,我们保证其行为完全符合 API 规范;同时这也意味着,部分功能在 Vapor 模式中被明确不支持:
- 选项式 API(Options API);
app.config.globalProperties;
- 在 Vapor 组件中调用
getCurrentInstance() 将始终返回 null;
- 元素级别的
@vue:xxx 生命周期事件。
此外,Vapor 模式中的自定义指令拥有不同的接口定义:
type VaporDirective = (
node: Element | VaporComponentInstance,
value?: () => any,
argument?: string,
modifiers?: DirectiveModifiers,
) => (() => void) | void
其中 value 参数是一个返回绑定值的响应式 getter 函数。开发者可以通过 watchEffect 建立响应式副作用(组件卸载时会自动清理),并可以选择返回一个清理函数。示例如下:
const MyDirective = (el, source) => {
watchEffect(() => {
el.textContent = source()
})
return () => console.log('cleanup')
}