「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」。
本文是对vue2,vue3,react中使用的diff算法规则进行总结。
key的作用
为虚拟dom元素添加唯一标识,方便在diff比对的时候快速找到指定dom
虚拟dom的好处
-
减少直接操作真实dom的次数
-
跨平台
vue2的diff
-
diff算法是同层级比较,不会跨层比较。然后依据新节点对旧节点进行增删改。
-
当遇到节点列表比较时,定义四个变量,:新节点列表的头索引A,新节点列表的尾索引B,旧节点列表的头索引C,旧节点列表的尾索引D。
-
然后将新旧节点列表按照以下四种方式依次对比,也称为双端比较。即新前==旧前,新后==旧后,新后==旧前,新前==旧后。
-
例如新前与旧前对比,此时A,B都为0,依次对比新旧列表元素,如果发现相同节点,就将AB加一,直至遇到不同的节点。当遇到不同的就换到下一个方式比较。
-
当新后与旧前对比时,使用isSameNode方法进行比较,如果是相同节点,需要将该旧节点移动到旧子节点列表的所有未处理节点的
最后面,如果不是,进行下一个方式的比较。 -
当新前与旧后对比时,如果是相同节点,需要将该旧节点移动到所有未处理节点的
最前面。 -
经过四种比较后:
- 如果新的节点列表被遍历完了,而旧的还有,需要将多于的旧的节点删除,并更新视图。
- 如果旧的节点列表遍历完了,而新的还有,需要根据多于的新的节点创建节点,并更新到视图中。
- 如果新旧节点列表都没有遍历完:
- 如果有key,旧节点列表建立key与索引的映射,遍历新节点列表,根据map映射找到对应节点进行比较patch。找到相同的节点进行patch,然后将该旧节点移动到剩余旧列表的未处理节点的前面。如果没有找到。如果没有找到,说明是新增节点,需要创建新节点到旧节点列表所有未处理节点的最前面。
- 如果没有key,遍历新列表,再遍历旧列表,使用sameVnode方法,找到相同的节点进行patch,然后将该旧节点移动到剩余旧列表的未处理节点的前面。如果没有找到,说明是新增节点,需要创建新节点到旧节点列表所有未处理节点的最前面。
- 最后删除旧节点列表中所有未处理节点。
vue3的diff
-
无key,
- 获取新旧节点列表的长度,根据短的length进行循环,对索引相同的节点进行patch。循环结束,如果新节点列表有剩余,新建节点到旧节点列表中,以及真实dom中。如果旧节点有剩余,删除真实dom中多余的节点。
-
有key,
- 从前往后双重循环,对比新旧节点列表。如果节点相同就进行patch
- 如果不同就跳出循环。从后往前遍历,对比新旧节点列表。如果节点相同就进行patch,如果不同就跳出循环。
- oldChildren遍历完了,newChildren还有剩余,新增节点。
- newChildren遍历完了,oldChildren还有剩余,删除节点。
- 新旧节点都有剩余,旧节点列表建立key与索引的映射,遍历新节点列表,根据map映射找到对应节点进行比较patch。如果没有找到,说明是新增节点,需要创建新节点到旧节点列表所有未处理节点的最前面。
react的diff
-
diff算法是同层级比较,不会跨层比较。
-
如果相同标签相同,只是修改了其类名,属性,样式等时。react会判定是同一元素,并更新变化的部分。而vue会判定为不同节点。
-
新旧子节点列表的对比
- 无key,
- 双重循环新旧子节点列表,如果发现有一对节点不一样,就删除旧节点列表中该旧节点及其后面所有节点。然后根据新节点列表新增节点。
- 有key,
- 将旧节点列表生成key与索引的映射,定义变量lastIndex = 0,
- 遍历新节点列表,在旧节点列表中找到对应key的元素。如果旧节点不存在,在当前旧节点列表的索引为lastIndex的位置创建新节点,不改变lastIndex。如果其索引小于lastIndex,则不移动该元素,并将其索引赋值给lastIndex,如果index ≥ lastIndex,将该旧节点移动到当前新节点的索引位置,lastIndex = index。继续遍历新节点列表下一个元素。
- 无key,
-
新列表遍历完后,遍历旧节点列表,如果不存在于新列表中,就删除掉。dff结束。
缺点:当将旧集合的最后一个元素移动到首位,react不是只移动该元素,而是按照上述规则将其他元素移动到该元素的后面。