写在前面:一场憋了三年的"蒸发"实验
2026 年 7 月,Vue 3.6 正式进入 RC 阶段。这个版本对 Vue 来说不是一次普通的 minor 升级,而是自 Vue 3.0 发布以来最大的一次底层换血:
- Vapor Mode(蒸汽模式):一条全新的编译产物路径,彻底绕开虚拟 DOM——没有 VNode、没有 diff、没有 patch,模板在编译期直接生成精确的原生 DOM 操作指令;
- 基于 alien-signals 的响应式重构:
@vue/reactivity底层被整个换掉,依赖追踪从"集合 + 遍历"换成"双向链表 + push-pull 混合传播",官方口径是内存占用显著下降、追踪效率大幅提升; - DefineComponent 类型简化:Vue 生态里最臭名昭著的复杂类型被重构,大型项目的类型检查耗时肉眼可见地缩短。
Vapor Mode 从 2023 年立项,到 2025 年 alpha 悄然上线,再到如今 RC 落地,尤雨溪和核心团队磨了三年。这三年里,前端圈发生了很多事:Solid 用细粒度响应式证明了"无虚拟 DOM"路线的性能上限,Svelte 5 用 Runes 完成了自己的信号化改造,React 则押注编译器(React Compiler)来自动优化虚拟 DOM 的开销。
所有主流框架都在回答同一个问题:运行时的开销,能不能在编译期消灭掉?
Vue 3.6 给出的答案是:能,而且可以渐进式地做——老代码一行不改继续跑虚拟 DOM,新组件加一个 vapor 标记就切换到无虚拟 DOM 路径,两种模式在同一个应用里共存。
这篇文章我会从虚拟 DOM 的历史包袱讲起,把 Vapor Mode 的编译策略、alien-signals 的数据结构、两种模式的互操作机制拆开揉碎讲清楚,配上编译产物级别的代码对比,最后给出实际项目的迁移建议。文章较长,建议收藏后配咖啡食用。
一、背景:虚拟 DOM 到底"欠"了我们什么
1.1 虚拟 DOM 的原罪不是慢,是"不知道什么变了"
先纠正一个流传已久的误解:虚拟 DOM 从来不是为了"快"而生的,它是为了声明式 UI 的开发体验而生的。React 当年的宣传语说得很诚实——虚拟 DOM "fast enough"(够快),而不是 "fastest"(最快)。
虚拟 DOM 的工作模型是这样的:
状态变化 → 重新执行渲染函数 → 生成新 VNode 树 → 与旧 VNode 树 diff → 计算出最小 DOM 操作 → patch 到真实 DOM
问题出在第 2~4 步。当你的组件里只有一个 {{ count }} 变了,虚拟 DOM 的做法是:
- 把整个组件的 VNode 树重新创建一遍(内存分配);
- 把新旧两棵树逐节点对比一遍(CPU 遍历);
- 最后发现:哦,原来只有一个文本节点要更新。
为了更新一个文本节点,你付出了"重建 + 全量对比"的代价。这就是虚拟 DOM 的结构性开销:它在运行时用暴力搜索来回答一个编译期就能回答的问题——"哪里会变?"
1.2 Vue 的模板:被低估的编译期情报库
Vue 相比 React 有一个先天优势:模板是静态可分析的。
JSX 本质是 JavaScript 表达式,编译器很难对它做深度静态分析——你可以在 JSX 里写任意的三元、map、函数调用,编译器只能保守处理。而 Vue 的模板是受限的 DSL,结构在编译期完全确定:
<template>
<div class="card">
<h1>{{ title }}</h1>
<p>静态文本,永远不变</p>
<button @click="count++">{{ count }}</button>
</div>
</template>
编译器看一眼就知道:
<div class="card">和<p>是纯静态的,一辈子不会变;<h1>的文本绑定了title,只有title变它才变;<button>的文本绑定了count,只有count变它才变。
Vue 3.0 时代,这些情报被用来做虚拟 DOM 的"补丁式优化":静态提升(hoistStatic)、补丁标记(patchFlags)、块树(Block Tree)。这些优化让 Vue 3 的虚拟 DOM diff 跳过了大量静态内容,性能已经相当能打。
但补丁终究是补丁——VNode 还是要创建,diff 还是要跑,运行时还是要背着整个虚拟 DOM 的包袱。于是一个自然的问题浮出水面:
既然编译期已经知道"哪里会变、怎么变",为什么不直接生成"精确更新代码",把虚拟 DOM 整个扔掉?
这就是 Vapor Mode 的核心思想。名字也起得很形象:让虚拟 DOM"蒸发"。
二、Vapor Mode:编译产物级拆解
2.1 一行标记开启新世界
Vapor Mode 的使用方式极其克制,在 <script setup> 上加一个 vapor 属性即可:
<script setup vapor>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">count: {{ count }}</button>
</template>
组件的写法完全不变:还是 ref、还是 computed、还是 watch,模板语法一个字都不用改。变的是编译产物。
如果整个应用都用 Vapor 组件,入口也换成专用 API,这样能把虚拟 DOM 运行时从产物里完全摇树掉:
import { createVaporApp } from 'vue'
import App from './App.vue'
createVaporApp(App).mount('#app')
2.2 编译产物对比:VNode 树 vs DOM 指令流
这是理解 Vapor Mode 最关键的一节。同一个模板,两种模式的编译产物完全是两个物种。
源模板:
<template>
<div>
<h1>{{ msg }}</h1>
<input :value="msg" @input="onInput" />
</div>
</template>
传统 VDOM 模式的编译产物(简化后):
import { createElementVNode as _createElementVNode,
toDisplayString as _toDisplayString,
openBlock as _openBlock,
createElementBlock as _createElementBlock } from 'vue'
export function render(_ctx) {
return (_openBlock(), _createElementBlock("div", null, [
_createElementVNode("h1", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
_createElementVNode("input", {
value: _ctx.msg,
onInput: _ctx.onInput
}, null, 40 /* PROPS, NEED_HYDRATION */, ["value"])
]))
}
注意它的本质:每次状态变化,这个 render 函数会整体重新执行,重新创建 VNode,然后交给运行时去 diff。1 /* TEXT */、40 /* PROPS */ 这些 patchFlags 是 Vue 3 的优化手段,用来告诉 diff 算法"这个节点只有文本/属性会变",但 diff 本身没有消失。
Vapor 模式的编译产物(简化后):
import { template as _template,
setText as _setText,
setValue as _setValue,
delegate as _delegate,
renderEffect as _renderEffect,
child as _child, next as _next } from 'vue/vapor'
// ① 静态模板一次性声明,运行时用 cloneNode 克隆
const t0 = _template("<div><h1></h1><input></div>", true)
export function render(_ctx) {
// ② 克隆静态结构,拿到根节点
const n0 = t0()
// ③ 编译期算好的路径,直接定位到动态节点
const n1 = _child(n0) // h1
const n2 = _next(n1) // input
// ④ 事件直接绑定(委托)
_delegate(n2, 'input', () => _ctx.onInput)
// ⑤ 为每个动态绑定创建独立的渲染副作用
_renderEffect(() => _setText(n1, _ctx.msg))
_renderEffect(() => _setValue(n2, _ctx.msg))
return n0
}
差异是根本性的:
- 没有 VNode。静态结构变成一个 HTML 字符串模板,运行时用
cloneNode(true)克隆——这是浏览器创建 DOM 最快的方式之一,比逐个createElement快得多; render只执行一次。它不是"每次更新都重跑的渲染函数",而是"组件挂载时执行一次的初始化函数";- 更新靠
renderEffect。每个动态绑定被编译成一个独立的响应式副作用,msg变化时,只有_setText(n1, msg)和_setValue(n2, msg)这两个函数会执行,直接命中目标 DOM 节点,中间没有任何 diff; - DOM 寻址在编译期完成。
_child(n0)、_next(n1)这些指令是编译器根据模板结构算好的"导航路径",运行时零搜索成本。
一句话总结:VDOM 模式是"每次更新重新描述整棵树,让运行时找不同";Vapor 模式是"编译期就把每个变化点的更新函数写好,更新时按名单精确打击"。
这个思路和 Solid 的编译策略高度同源,但 Vue 的独特之处在于:它是在一个已有海量存量代码的框架里,把这条路径做成了可选项而不是断代式革命。
2.3 指令、插槽、组件树:细节里的魔鬼
把简单模板编译成 DOM 指令不难,难的是 Vue 模板那一大堆特性怎么在无 VNode 的世界里活下来。挑几个有代表性的说:
v-if / v-for: 在 VDOM 世界里,条件和列表就是"生成不同的 VNode 子树,交给 diff"。Vapor 里没有 diff,所以编译器为它们生成专门的"块管理器"——createIf 维护一个可切换的 DOM 片段(挂载/卸载分支时直接操作锚点之间的节点),createFor 则内置了自己的 key 追踪与最小移动算法,直接搬运真实 DOM 节点。也就是说,列表 diff 没有消失,而是从"通用 VNode diff"退化成了"专用列表调和",作用域小得多,也快得多。
插槽(slots): 插槽在 VDOM 里是"传递 VNode 工厂函数"。Vapor 里插槽变成传递"DOM 片段工厂",父组件把编译好的块函数传给子组件,子组件在正确的锚点位置调用它。作用域插槽的参数传递用闭包 + 响应式包装解决,用法层面无感。
组件实例: Vapor 组件的实例被大幅瘦身。VDOM 组件实例上挂着 vnode、subTree、update effect 等一大堆字段;Vapor 组件不需要这些,实例化成本显著降低。官方在早期分享中给过一个数字:基准测试里 Vapor 模式可以在约 100ms 内挂载 10 万个组件——这类"海量小组件"场景(大表格、树形控件、编辑器)正是 VDOM 模式的传统痛点。
2.4 两种模式共存:vaporInteropPlugin
对存量项目来说,最关键的问题不是"Vapor 多快",而是"我能不能只把最痛的那几个组件切过去"。Vue 3.6 的答案是互操作层:
import { createApp, vaporInteropPlugin } from 'vue'
import App from './App.vue'
createApp(App)
.use(vaporInteropPlugin) // 开启 VDOM ↔ Vapor 互操作
.mount('#app')
装上这个插件后:
- VDOM 组件可以渲染 Vapor 子组件:运行时会把 Vapor 组件包进一个特殊的 VNode 壳里,diff 时把它当"黑盒"处理;
- Vapor 组件也可以渲染 VDOM 子组件:反向包装,Vapor 的块管理器负责这个 VDOM 孤岛的挂载和卸载;
- props、事件、插槽、provide/inject 跨模式透传。
这个设计非常"Vue":**用一层薄薄的适配器,换来渐进式迁移的自由度。**代价是互操作边界上有一点包装开销,所以官方建议是按"子树"为单位切换,而不是在组件树里 VDOM/Vapor 交错混编。
典型的落地策略:
应用外壳(路由、布局、低频更新)→ 留在 VDOM 模式,生态兼容性最大化
数据大屏 / 万行表格 / 实时行情 / 编辑器内核 → 切 Vapor,吃满性能红利
三、alien-signals:响应式系统的底盘换血
Vapor Mode 拿到的所有性能红利,都建立在一个前提上:响应式系统本身要够快。因为在 Vapor 世界里,"一个动态绑定 = 一个 renderEffect",副作用的数量比 VDOM 时代(一个组件一个 render effect)多了一个数量级。如果依赖追踪本身不够快,Vapor 就是空中楼阁。
这就是 Vue 3.6 引入 alien-signals 的原因。
3.1 老响应式系统的账本:Set 的代价
Vue 3.0~3.4 的响应式系统,依赖关系是用"集合"来记的,概念上长这样:
// 每个响应式属性 → 一个 Dep(Set 结构),存着订阅它的所有 effect
targetMap: WeakMap<object, Map<key, Set<ReactiveEffect>>>
// 每个 effect → 一个数组,存着它依赖的所有 Dep
effect.deps: Dep[]
这套结构直观好懂,但有几个性能账单:
- 重新收集的开销:effect 每次重新执行,都要先把自己从所有旧 Dep 里删掉,再重新添加(Vue 3.4 用版本计数优化过,但结构性成本还在);
- Set 的内存与遍历成本:JS 的 Set 是哈希结构,单个条目的内存开销远大于链表节点,触发更新时遍历 Set 也比顺着链表走慢;
- 传播模型偏"推"(push):属性一变就把所有下游 effect 推进调度队列,对于"钻石依赖"(一个源被多条 computed 路径引用,最终汇聚到同一个 effect)这类拓扑,容易产生重复计算或需要额外机制去重。
对 VDOM 模式来说这些开销还能接受——毕竟 effect 数量少(组件级)。但 Vapor 把 effect 打散到绑定级,账单就要翻倍地付。
3.2 alien-signals 的三板斧
alien-signals 最初是 Vue 语言工具(Volar)作者在优化编辑器性能时孵化的独立信号库,在各家 signal 库的基准测试里长期名列前茅,后来被正式吸收进 Vue 3.6 的 @vue/reactivity 重构中。它快的原因可以归纳为三点:
① 双向链表取代 Set
依赖关系不再用 Set 存,而是用"链接节点(Link)"串成双向链表:
每个 Link 节点同时挂在两条链上:
- 订阅者链:某个 signal 的所有订阅者(sub1 ↔ sub2 ↔ sub3)
- 依赖链:某个 effect 的所有依赖(dep1 ↔ dep2 ↔ dep3)
好处非常实在:
- 插入/删除都是 O(1) 的指针操作,没有哈希计算;
- Link 节点可以池化复用,effect 重新执行时旧节点原地复用,几乎零 GC 压力;
- 触发更新就是顺着链表走一遍,缓存局部性比哈希桶好得多。
② push-pull 混合传播
纯 push(一变就全量通知到底)会重复计算,纯 pull(用的时候才逐层检查脏没脏)会有惰性检查开销。alien-signals 用的是混合模型:
- push 阶段:源 signal 变化时,只顺着订阅链把下游节点标脏(打个 dirty/pending 标记),不立即重算;
- pull 阶段:当某个 effect 真正要执行(或 computed 被读取)时,才反向检查上游:"我的依赖真的变了吗?"——如果一条 computed 链算下来值没变,下游直接跳过。
这套模型天然解决钻石依赖问题:无论多少条路径汇聚,每个节点在一轮更新里最多计算一次,且值未变化的分支会被剪枝。
③ 为引擎优化的扁平数据结构
alien-signals 的实现刻意规避了递归调用(用迭代 + 显式栈替代)、规避了 Array/Set/Map 等高级容器、把状态压缩成位标记(flags)存在节点上。这些约束看起来"反人类",但对 V8 这类引擎极其友好:对象形状稳定、内联缓存命中率高、没有隐藏类漂移。
3.3 对使用者意味着什么
最妙的地方在于:API 层面几乎零变化。ref、computed、watch、reactive 的行为语义保持兼容,你升级到 3.6,什么都不改,响应式系统就换了引擎。官方给出的量级是:依赖追踪的内存占用和传播开销都有明显下降,对 computed 链很深、watch 很多的大型应用尤其明显。
对生态开发者还有一层意义:alien-signals 本身是独立库,Pinia、VueUse 这类库的底层,以及其他框架甚至语言(社区已有多语言移植)都可以复用这套信号内核。信号(signal)正在事实上成为跨框架的响应式通用协议,而 alien-signals 是目前这个赛道里性能最能打的实现之一。
四、代码实战:把一个高频更新组件切到 Vapor
纸上谈兵不够,来个能落地的例子:一个实时刷新的行情列表——典型的"高频更新 + 大量重复结构"场景,VDOM 的重灾区。
4.1 组件代码
<!-- TickerList.vue -->
<script setup vapor>
import { ref, onMounted, onUnmounted } from 'vue'
interface Tick {
symbol: string
price: number
change: number
}
const ticks = ref<Tick[]>([])
let timer: number
function mockFeed() {
// 模拟每 16ms 一批行情推送,随机更新 50 条
const next = ticks.value.slice()
for (let i = 0; i < 50; i++) {
const idx = (Math.random() * next.length) | 0
const t = next[idx]
if (!t) continue
const delta = (Math.random() - 0.5) * 2
next[idx] = { ...t, price: +(t.price + delta).toFixed(2), change: +delta.toFixed(2) }
}
ticks.value = next
}
onMounted(() => {
ticks.value = Array.from({ length: 2000 }, (_, i) => ({
symbol: `SYM${String(i).padStart(4, '0')}`,
price: 100 + Math.random() * 50,
change: 0,
}))
timer = window.setInterval(mockFeed, 16)
})
onUnmounted(() => clearInterval(timer))
</script>
<template>
<div class="ticker-list">
<div
v-for="t in ticks"
:key="t.symbol"
class="row"
:class="{ up: t.change > 0, down: t.change < 0 }"
>
<span class="symbol">{{ t.symbol }}</span>
<span class="price">{{ t.price.toFixed(2) }}</span>
<span class="change">{{ t.change > 0 ? '+' : '' }}{{ t.change.toFixed(2) }}</span>
</div>
</div>
</template>
注意:除了 <script setup vapor> 这一个词,这就是一个普普通通的 Vue 组件。逻辑、模板、样式绑定全是熟悉的写法。这是 Vapor Mode 最大的产品智慧——迁移成本被压到了地板上。
4.2 两种模式下发生了什么
VDOM 模式下,每 16ms 一次的更新流程:
ticks.value = next触发组件的 render effect;- 重新执行 render,为 2000 行生成 2000 组 VNode(哪怕只有 50 行的数据变了);
- 带 key 的列表 diff:逐行对比新旧 VNode,找出 50 个变化行;
- 对变化行做 patch,更新文本和 class。
Vapor 模式下:
ticks.value = next触发createFor块的列表调和;- 按 key 对齐后,只有数据引用变化的那 50 行的 renderEffect 被标脏;
- 每行内部:price 文本、change 文本、class 各自是独立的精确更新指令,直接写 DOM。
同样的更新,Vapor 路径跳过了"2000 组 VNode 创建 + 全列表结构 diff"这两座大山。在这类场景下,CPU 火焰图上原本占大头的 patchKeyedChildren 和 VNode 分配基本消失,帧预算的富余量肉眼可见。如果你维护过行情、监控大屏或者日志流组件,应该明白这意味着什么——以前需要上虚拟滚动 + 手动 diff 才能压住的场景,现在框架层直接给你兜住了一大半。
4.3 几个实战注意点
从 alpha 到 RC 阶段的社区实践里,有几个坑值得提前知道:
this与选项式 API:Vapor Mode 目前面向<script setup>组合式 API 设计,选项式 API 组件不能直接标记 vapor。老项目里的 Options API 组件留在 VDOM 模式即可,靠互操作层通信;- 依赖 VNode 的库要甄别:直接操作 vnode 的库(某些高阶组件、渲染函数魔改型插件)在 Vapor 组件内不可用。上 Vapor 之前,先盘点目标子树用到的第三方组件;
- 自定义指令:Vapor 下自定义指令拿到的是真实 DOM 元素和简化的绑定信息,绝大多数"操作 DOM 型"指令(如 v-focus、v-lazy)可以无缝工作,但依赖 VNode 钩子细节的指令需要适配;
- SSR 与水合:Vapor 组件的服务端渲染与水合路径在 3.6 周期内持续完善,重 SSR 的项目建议在灰度环境充分验证后再切核心页面;
- 不要为了切而切:低频更新的表单页、配置页,VDOM 模式的开销本来就可以忽略,切 Vapor 收益趋近于零。把子弹留给真正的热点组件。
五、性能之外:这次重构的三层深意
5.1 包体:运行时终于可以"按需付费"
虚拟 DOM 运行时(VNode 创建、diff、patch、调度)在 Vue 的产物里占了相当的体积。纯 Vapor 应用用 createVaporApp 入口后,这部分可以被完整摇树掉,核心运行时体积大幅缩小。对性能预算严苛的场景——嵌入式 WebView、低端机 H5、微前端子应用——这是实打实的首屏收益。
有个值得玩味的对比:Svelte 走的是"编译器即框架",运行时极小但每个组件都携带自己的更新代码,组件多了产物反而膨胀;Vue Vapor 是"共享指令集 + 编译期路径规划",运行时保留一个精简的 DOM 指令库,组件产物只是指令调用序列。**在大型应用里,后者的产物增长曲线更平缓。**这是 Vue 在编译式框架赛道上后发制人的一手。
5.2 心智模型:Vue 悄悄完成了"信号化"
如果你把 Vue 3.6 的技术栈铺开看:
alien-signals(信号内核)
→ ref/computed(信号的 Vue 语法糖)
→ renderEffect(绑定级细粒度副作用)
→ 直接 DOM 更新(无虚拟层)
这就是一个标准的**细粒度响应式(fine-grained reactivity)**架构,和 Solid 的心智模型已经完全同构。区别在于:Solid 从第一天就是这个模型,Vue 是带着整个存量生态、用五年时间平滑演化过来的——而且用户几乎无感。
ref 当年被吐槽 .value 麻烦,现在回头看,正是这层显式的信号容器抽象,让 Vue 可以在底层随意更换实现(Set 版 → 版本计数版 → alien-signals 版)而 API 纹丝不动。好的抽象边界,就是你给未来自己留的活口。
5.3 框架战争的下一回合:编译期情报战
2026 年的前端框架竞争,本质上已经变成"谁能在编译期榨取更多情报":
- React:JSX 太自由,静态分析难做,于是搞 React Compiler 在编译期自动插 memo——方向是"让 VDOM 跑得更少";
- Svelte 5:Runes 把响应式声明变成编译期可见的语法结构——方向是"编译期直出更新代码";
- Solid:模板编译 + 细粒度信号,无 VDOM 的原教旨主义;
- Vue 3.6:模板静态分析 + 信号内核 + 双模式共存——方向是"两条路都修好,你自己选"。
Vue 的打法未必在单项 benchmark 上称王,但它选了一条对存量用户最厚道的路:**革命只发生在编译器和运行时内部,用户的代码和心智保持连续。**对一个有着海量线上项目的框架来说,这可能比 benchmark 上多几个百分点重要得多。
六、迁移决策指南:什么时候上车
给不同处境的团队一个直白的建议矩阵:
新项目、性能敏感(大屏/行情/编辑器/移动端 H5)
直接按 Vapor 优先设计:组合式 API + <script setup vapor>,入口用 createVaporApp,第三方组件选型时优先确认 Vapor 兼容性。
存量大型项目、有明确性能痛点
升级 3.6 先白嫖 alien-signals 的响应式提升(零改动);然后装 vaporInteropPlugin,只把火焰图上最热的几个组件子树切成 Vapor。小步快跑,一个子树一个子树验证。
存量项目、无性能痛点
升级 3.6,享受响应式重构和类型检查提速,Vapor 一个字都不用碰。它是可选项,不是作业。
库作者
现在就该行动:检查你的库是否直接依赖 VNode 内部结构,提供 Vapor 兼容说明。未来两年"是否 Vapor-ready"大概率会成为组件库选型的硬指标。
升级前的检查清单:
# 1. 升级依赖
npm i vue@^3.6 vite @vitejs/plugin-vue@latest
# 2. 全局搜索高风险 API 使用点
grep -rn "getCurrentInstance\|\.vnode\|subTree" src/
# 3. 灰度开启:先在独立路由/独立子应用上启用 vapor 组件
# 4. 对比火焰图:Performance 面板录制切换前后的更新热点
七、总结与展望
Vue 3.6 值得记住的三件事:
- Vapor Mode 把"无虚拟 DOM"从别家框架的卖点,变成了 Vue 用户的一个开关。一行
vapor标记,存量语法零改动,编译产物从"VNode 工厂"切换成"DOM 指令流",高频更新场景的结构性开销被连根拔掉; - alien-signals 完成了响应式系统的底盘换血。双向链表依赖图 + push-pull 混合传播 + 引擎友好的扁平结构,让"绑定级细粒度副作用"这个 Vapor 的前提条件在性能上站得住;
- 渐进式依然是 Vue 最锋利的武器。VDOM 与 Vapor 双模式共存、互操作层兜底、API 语义全兼容——架构级革命被包装成了一次普通的 minor 升级。
再往前看,两个方向值得盯住:一是 Vapor 生态的成熟速度——主流组件库完成 Vapor 适配之日,才是它真正大规模落地之时;二是信号协议的跨框架标准化——TC39 的 Signals 提案还在推进,alien-signals 这类高性能实现很可能反过来影响标准的形状。
虚拟 DOM 不会立刻消失,它在低频更新、高度动态结构的场景里依然是省心的默认解。但 2026 年之后,"要不要虚拟 DOM"在 Vue 里不再是信仰问题,而是一个按组件粒度做的工程决策。
框架把选择权还给了你——这大概就是这三年等待最好的回报。
本文基于 Vue 3.6 RC 阶段的公开信息与编译产物分析写成,细节可能随正式版微调,落地前请以官方文档为准。