组件更新
在组件化章节,我们介绍了 Vue 的组件化实现过程,不过我们只讲了 Vue 组件的创建过程,并没有涉及到组件数据发生变化,更新组件的过程。而通过我们这一章对数据响应式原理的分析,了解到当数据发生变化的时候,会触发渲染 的回调函数,进而执行组件的更新过程,接下来我们来详细分析这一过程。
组件的更新还是调用了 方法,我们再回顾一下这个方法,它的定义在 中:
组件更新的过程,会执行 ,它仍然会调用 函数,在 中定义:
这里执行 的逻辑和首次渲染是不一样的,因为 不为空,并且它和 都是 VNode 类型,接下来会通过 判断它们是否是相同的 VNode 来决定走不同的更新逻辑:
的逻辑非常简单,如果两个 的 不相等,则是不同的;否则继续判断对于同步组件,则判断 、、 类型等是否相同,对于异步组件,则判断 是否相同。
所以根据新旧 是否为 ,会走到不同的更新逻辑,我们先来说一下不同的情况。
新旧节点不同
如果新旧 不同,那么更新的逻辑非常简单,它本质上是要替换已存在的节点,大致分为 3 步
创建新节点
以当前旧节点为参考节点,创建新的节点,并插入到 DOM 中, 的逻辑我们之前分析过。
更新父的占位符节点
我们只关注主要逻辑即可,找到当前 的父的占位符节点,先执行各个 的 的钩子函数,如果当前占位符是一个可挂载的节点,则执行 的 钩子函数。对于这些钩子函数的作用,在之后的章节会详细介绍。
删除旧节点
把 从当前 DOM 树中删除,如果父节点存在,则执行 方法:
删除节点逻辑很简单,就是遍历待删除的 做删除,其中 的作用是从 DOM 中移除节点并执行 的 钩子函数,并对它的子节点递归调用 函数; 是执行 的 钩子函数以及 的 钩子函数,并对它的子 递归调用 函数; 就是调用平台的 DOM API 去把真正的 DOM 节点移除。
在之前介绍组件生命周期的时候提到 这两个生命周期钩子函数,它们就是在执行 过程中,执行了 的 钩子函数,它的定义在 中:
当组件并不是 的时候,会执行 方法,然后就会执行 两个钩子函数。
新旧节点相同
对于新旧节点不同的情况,这种创建新节点 -> 更新占位符节点 -> 删除旧节点的逻辑是很容易理解的。还有一种组件 的更新情况是新旧节点相同,它会调用 方法,它的定义在 中:
的作用就是把新的 到旧的 上,这里我们只关注关键的核心逻辑,我把它拆成四步骤:
执行 钩子函数
当更新的 是一个组件 的时候,会执行 的方法,它的定义在 中:
方法就是拿到新的 的组件配置以及组件实例,去执行 方法,它的定义在 中:
的逻辑也非常简单,由于更新了 ,那么 对应的实例 的一系列属性也会发生变化,包括占位符 的更新、 的更新, 的更新, 的更新等等。
执行 钩子函数
回到 函数,在执行完新的 的 钩子函数,会执行所有 的 钩子函数以及用户自定义 钩子函数,对于 的钩子函数,之后我们会有具体的章节针对一些具体的 case 分析。
完成 过程
如果 是个文本节点且新旧文本不相同,则直接替换文本内容。如果不是文本节点,则判断它们的子节点,并分了几种情况处理:
与 都存在且不相同时,使用 函数来更新子节点,这个后面重点讲。
2.如果只有 存在,表示旧节点不需要了。如果旧的节点是文本节点则先将节点的文本清除,然后通过 将 批量插入到新节点 下。
3.如果只有 存在,表示更新的是空节点,则需要将旧的节点通过 全部清除。
4.当只有旧节点是文本节点的时候,则清除其节点文本内容。
执行 钩子函数
再执行完 过程后,会执行 钩子函数,它是组件自定义的钩子函数,有则执行。
那么在整个 过程中,最复杂的就是 方法了,下面我们来单独介绍它。
updateChildren
的逻辑比较复杂,直接读源码比较晦涩,我们可以通过一个具体的示例来分析它。
当我们点击 按钮去改变数据,最终会执行到 去更新 部分的列表数据,我们通过图的方式来描述一下它的更新过程:
第一步:
第二步:
第三步:
第四步:
第五步:
第六步:
总结
组件更新的过程核心就是新旧 vnode diff,对新旧节点相同以及不同的情况分别做不同的处理。新旧节点不同的更新流程是创建新节点->更新父占位符节点->删除旧节点;而新旧节点相同的更新流程是去获取它们的 children,根据不同情况做不同的更新逻辑。最复杂的情况是新旧节点相同且它们都存在子节点,那么会执行 逻辑,这块儿可以借助画图的方式配合理解。
领取专属 10元无门槛券
私享最新 技术干货