# 前言
在开启本篇章之前,我们先来思考一个问题,假设有以下模板:
html
复制代码<template>
<p>hello world</p>
<p>{{ msg }}</p>
</template>>
其中一个 p 标签的节点是一个静态的节点,第二个 p 标签的节点是一个动态的节点,如果当 msg 的值发生了变化,那么理论上肉眼可见最优的更新方案应该是只做第二个动态节点的 diff,而无需进行第一个 p 标签节点的 diff。
如果熟悉 Vue 2.x 的小伙伴可能会知道,在 Vue 2.x 版本中在编译过程中有一个叫做 optimize 的阶段,会进行标记静态根节点的操作,被标记为静态根节点的节点,一方面会生成一个 staticRenderFns,首次渲染会以这个静态根节点 vnode 进行缓存,后续渲染会直接取缓存中的,从而避免重复渲染;另一方面生成的 vnode 会带有 isStatic = true 的属性,将会在 diff 过程中被跳过。但 Vue 2.x 对静态节点进行缓存就是一种空间换时间的优化策略,为了避免过度优化,在 Vue 2.x 中,识别静态根节点是需要满足:
- 子节点是静态节点;
- 子节点不是只有一个静态文本节点的节点。
所以,上面的示例第一个 p 标签在 Vue 2.x 中不会被判定位静态根节点,也就无法进行优化。
关于
Vue 2.x如何做的编译时优化,这里只是简单进行了介绍,想了解更多的小伙伴可以参考这里:入口开始,解读 Vue2 源码(七)—— $mount 内部实现 — compile optimize标记节点。
那么 Vue 3 呢?还是和 Vue 2 一样吗?答案显然是否定的,首先我们前面介绍了对于静态的节点,Vue 3 首先会进行静态提升,也就是相当于缓存了静态节点的 vnode,那 diff 过程呢?会跳过吗?本小节我们来一探究竟。
# PatchFlags
# 是什么?
首先,我们需要认识一个 PatchFlags 这个属性,它是一个枚举类型,里面是一些二进制操作的值,用来标记在节点的 patch 类型。具体的枚举内容如下:
export const enum PatchFlags {
// 动态文本的元素
TEXT = 1,
// 动态 class 的元素
CLASS = 1 << 1,
// 动态 style 的元素
STYLE = 1 << 2,
// 动态 props 的元素
PROPS = 1 << 3,
// 动态 props 和有 key 值绑定的元素
FULL_PROPS = 1 << 4,
// 有事件绑定的元素
HYDRATE_EVENTS = 1 << 5,
// children 顺序确定的 fragment
STABLE_FRAGMENT = 1 << 6,
// children 中有带有 key 的节点的 fragment
KEYED_FRAGMENT = 1 << 7,
// 没有 key 的 children 