React diff——差异协调算法简介
差异协调算法
使用递增法,通过对比新的列表中的节点,在原本的列表中的位置是否是递增,来判断当前节点是否需要移动;
通过标记和优先级系统来安排和优化更新任务;
对比新旧两个虚拟DOM树的变更差异,将更新补丁作用于真实的DOM,以最小成本完成视图更新;
触发更新在state变化与hooks调用之后,虚拟dom树变更遍历,深度优化、分治。
核心
- 层级比较
- key值优化
- 双指针遍历:先对比新旧列表的首尾元素,若匹配则移动指针,否则通过key建立旧元素的hash表,快速查找新元素在旧元素列表中的位置
- 类型不同则直接替换
策略
- 同层比较
- 最小化操作
层级比较
- 树对比:对树逐层比较,只对同一层次的节点比较,直接替换不同类型的根节点,提升对比效率
- 组件对比:若组件同一类型,则继续比较(更新其props并触发生命周期方法);否则直接销毁旧组件,创建新实例。所以只要父组件类型不同,子组件就会被重新渲染
- 元素对比:同层级中,通过标记节点操作生成补丁。节点操作包括插入、移动、删除等,其中节点重新排序同时涉及三个操作,效率消耗最大,通过双指针遍历和标记key,可以直接移动DOM节点,降低内耗
流程:
-
真实DOM映射为虚拟DOM
-
当虚拟DOM发生变化后,元素对比逻辑:
- 对新集合进行循环遍历,从左向右移动指针
- 找到需要移动的节点: 通过唯一key来判断老集合中是否存在相同的节点,没有则创建,有并且if(preChild === nextChild),会将节点在新集合中的位置和老集合中lastIndex进行比较
- 移动节点: 如果小于,则进行移动操作,否则不进行移动操
这是一种顺序优化手段,lastIndex一直在更新,表示访问过的节点在老集合中最左的位置,即最大的位置,如新集合中当前访问的节点比lastIndex大,说明当前访问节点在老集合中就比上一个节点位置靠后,则该节点不会影响其他节点的位置,因此不用添加到差异队列中
- 添加节点、移除节点: 如果遍历过程中,新集合没有,老集合中有的节点,进行删除操作
根据差距计算生成patch,这个patch是个结构化的数据,内容包含了增加、更新、移除等
-
根据patch更新真实的DOM,反馈到用户界面。
dom更新时:
- 批量执行变更,减少重绘
- 尽量复用已有dom节点,减少dom操作
- 优化:空间换时间,将key与index的关系维护成一个map