此篇抽象对比了两框架diff算法,有时间深入研究react diff算法逻辑,不然理解欠深
后续会从各角度对比React和Vue,除了此文,目前已有的:
虚拟DOM对象包含以下属性:
- sel:选择器
- data:绑定的数据(attribute/props/eventlistner/class/dataset/hook)
- children:子节点数组
- text:当前text节点内容
- elm: 对真实dom element的引用
- key:用于优化DOM操作
diff算法比较变量
vue维护四个变量:
- oldStartIdx => 旧头索引
- oldEndIdx => 旧尾索引
- newStartIdx => 新头索引
- newEndIdx => 新尾索引
两边方向同时比较,由新的位置来获取真实DOM;如果老的先走完,就添加;如果新的先走完,就删除
react维护三个变量:
- nextIndex => 遍历nextChildren时候的index,每遍历一个元素加1 (遍历新节点)
- lastPlacedIndex => 上一次从prevChildren中取出来元素时,这个元素在prevChildren中的index(新节点对应)
- oldIndex => 元素在数组中的位置(旧节点对应的位置)
节点移动前提是:oldIndex < lastPlacedIndex
移动的原则:
- 首个节点(指的是新节点)不执行移动操作(除非它要被移除),以该节点为原点,其它节点都去寻找自己的新位置;
- 将原来的元素往右移,通过lastIndex来控制。在lastIndex左边的,就往lastIndex右边移动;在lastIndex右边的,就不需要动。
nextIndex : 往右遍历,逐渐+1
oldIndex:根据nextIndex在旧结点中寻找对应的元素,如果能找到比较oldIndex和lastPlacedIndex,否则添加
lastPlacedIndex:可以理解为是一个比较指引,当发生移动的时候lastPlacedIndex值保持上一次比较值,不做移动使用oldIndex值
oldIndex = 4 > lastPlacedIndex 不做操作,且 lastPlacedIndex = oldIndex = 4
oldIndex = 0 < lastPlacedIndex 移动, lastPlacedIndex保持不变
diff算法比对方式
-
vue的列表比对,采用从两端到中间的比对方式,而react则采用从左到右依次比对的方式。当一个集合,只是把最后一个节点移动到了第一个,react会把前面的节点依次移动,而vue只会把最后一个节点移动到第一个。总体上,vue的对比方式更高效。
-
vue比对节点,当节点元素类型相同,但是className不同,认为是不同类型元素,删除重建,而react会认为是同类型节点,只是修改节点属性
react只比较节点类型和key
function sameVnode(vnode1: VNode, vnode2: VNode): boolean {
return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel;
}
vue比较节点类型和key,还有属性
function sameVnode (a, b) {
return (
a.key === b.key && // key值
a.tag === b.tag && // 标签名
a.isComment === b.isComment && // 是否为注释节点
// 是否都定义了data,data包含一些具体信息,例如onclick , style
isDef(a.data) === isDef(b.data) &&
sameInputType(a, b) // 当标签是<input>的时候,type必须相同
)
}
diff算法遍历原理
-
React 首位是除删除外是固定不动的,然后依次遍历对比;
-
Vue 的compile 阶段的
optimize标记了static 点,可以减少 differ 次数,而且是采用双向遍历方法;
diff算法跟新DOM逻辑
-
Vue基于snabbdom库,它有较好的速度以及模块机制。Vue Diff使用双向链表,边对比,边更新DOM。
-
React主要使用diff队列保存需要更新哪些DOM,得到patch树,再统一操作批量更新DOM。
相同点
-
虚拟DOM在比较时只比较同一层级节点,复杂度都为 O(n),降低了算法复杂度;
-
都使用key比较是否是相同节点,都是为了尽可能的复用节点
-
都是操作虚拟DOM,最小化操作真实DOM,提高性能(其实虚拟DOM的优势 并不是在于它操作DOM快)
-
都是不要用 index作为 key