17.1.4 渲染器的运行时支持
编译产物只是「信息」,真正提速要靠渲染器:更新元素时优先遍历
dynamicChildren(patchBlockChildren),并依据patchFlags做 props 的靶向更新。
一、patchElement 的两处改造
patchElement 取自渲染器部分,为支持编译优化需新增两件事:
- 优先检测
dynamicChildren:存在则直接调用patchBlockChildren完成更新,否则走原来的patchChildren; - 检测补丁标志实现 props 靶向更新:避免全量 props 更新,最大化提升性能。
function patchElement(n1, n2) {
const el = (n2.el = n1.el)
const oldProps = n1.props
const newProps = n2.props
if (n2.patchFlags) {
// 靶向更新:只做标志位声明的那些更新
if (n2.patchFlags & PatchFlags.CLASS) {
patchClass(el, newProps.class)
} else if (n2.patchFlags & PatchFlags.STYLE) {
patchStyle(el, oldProps.style, newProps.style)
}
// else if (...) { /* ... */ }
} else {
// 无补丁标志 → 全量更新
for (const key in newProps) {
if (newProps[key] !== oldProps[key]) {
patchProps(el, key, oldProps[key], newProps[key])
}
}
for (const key in oldProps) {
if (!(key in newProps)) {
patchProps(el, key, oldProps[key], null)
}
}
}
if (n2.dynamicChildren) {
// 有动态节点集合 → 只更新动态节点
patchBlockChildren(n1, n2)
} else {
// 在处理 children 时调用 patchChildren
patchChildren(n1, n2, el)
}
}
function patchBlockChildren(n1, n2) {
// 只更新动态节点即可
for (let i = 0; i < n2.dynamicChildren.length; i++) {
patchElement(n1.dynamicChildren[i], n2.dynamicChildren[i])
}
}
二、这样为什么快
- 更新 Block 时跳过静态子树,不再逐个比较静态节点;
- 更新动态节点时只做该节点真正需要的更新(文本 / class / style / props),而不是遍历全部 props;
- 原本「按层级遍历整棵树」的
patchChildren开销被压缩成对dynamicChildren的一次遍历。
前提是更新前后
dynamicChildren的结构与顺序一致;一旦模板结构不稳定(v-if、v-for),这套机制就会失效,见 17.2 各节。
相关
- 17.1.3 收集动态节点:
dynamicChildren与补丁标志的由来 - 17.2.1 带有 v-if 指令的节点:结构不稳定时
dynamicChildren会失效