vue3处理组件
虽然 Vue.js 的更新粒度是组件级别的,组件的数据变化只会影响当前组件的更新但是在组件更新的过程中,也会对子组件做一定的检查,判断子组件是否也要更新并通过某种机制避免子组件重复更新
updatecomponent函数
如果 shouldUpdateComponent 返回true,那么在它的最后先执行invalidateJob(instance.update)避免子组件由于自身数据变化导致的重复更亲然后又执行了子组件的副作用渲染函数instance.update 来主动触发子组件的更新
一个组件重新渲染可能会有两种场景:
一种是组件本身的数据变化,这种情况下next是 null
另一种是父组件在更新的过程中,遇到子组件节点,先判断子组件是否需要更新,如果需要则主动执行子组件的重新渲染方法,这种情况下next 就是新的子组件 vnode
这个子组件对应的新的组件 vnode 是什么时候创建的呢?
在父组件重新渲染的过程中
通过 renderComponentRoot 渲染子树vnode 的时候生成
因为子树 vnode 是个树形结构
通过遍历它的子节点就可以访问到其对应的组件 vnode
总结:处理组件的过程
processComponent处理组件vnode,本质上就是去判断子组件是否需要更新
如果需要则递归执行子组件的副作用渲染函数来更新,否则仅仅更新一些vnode 的属性
并让子组件实例保留对组件 vnode的引用,用于子组件自身数据变化引起组件重新渲染的时候在渲染函数内部可以拿到新的组件 vnode
处理普通元素
diff算法
第一步,从头部开始同步头部
第二步从尾部开始同步尾部
剩下3种情况
新子节点有剩余要添加的新节点
旧子节点有剩余要删除的多余节点
未知子序列
处理未知子序列
当两个节点类型相同时,执行更新操作
当新子节点中没有旧子节点中的某些节点时,执行删除操作
当新子节点中多了旧子节点中没有的节点时,执行添加操作
相对来说这些操作中最麻烦的就是移动,既要判断哪些节点需要移动也要清楚如何移动
var prev=[1,2,3,4,5,6]
var next=[1,3,2,6,4,5]
移动子节点
对比新旧子序列,则需要遍历某个序列如果在遍历旧子序列的过程中需要判断某个节点是否在新子序列中存在
这就需要双重循环双重循环的复杂度是 O(n2),为了优化这个复杂度,建立索引图,把时间复杂度降低到0(n)
建立索引图
在开发过程中,会给 v-for生成的列表中的每一项分配key作为项的唯一 ID
这个 key 在 diff 过程中起到很关键的作用
对于新旧子序列中的节点,key相同的就是同一个节点,直接执行patch 更新即可
最复杂的就是求解做场递增子序列
求解最长递增子序列是一道经典的算法题
多数解法是使用动态规划的思想,算法的时间复杂度是 0(n2)
而 Vue.js 内部使用的是维基百科提供的一套“贪心+二分查找”的算法贪心算法的时间复杂度是 0(n)
二分查找的时间复杂度是 0(logn),总时间复杂度是 0(nlogn)
主要目的
让递增序列的差尽可能的小,从而可以获得更长的递增子序列,是一种贪心算法的思想
注意,求解的是最长子序列索引值,它的每个元素其实对应的是数组的下标
流程附图