diff算法过程:触发更新,打补丁,生成补丁
一、react中的diff算法
将单一节点比对转化为三种类型的比对:
树(删除和创建)
两棵树在同层级比较,当发现该节点不存在,直接删除这个节点及其子节点
组件(删除和创建)
- 同类型组件,进行树比对;如果vdom没有变化,可以使用shouldComponentUpdate()判断是否需要diff
- 非同类型的,直接替换到补丁中
元素(删除、创建、移动)
- 没有key的情况下,对新老集合对比,如果不同,则删除老的,创建新的
- 有key的情况下,对新老集合进行遍历:
- 如果oldIndex<newIndex,则移动;
- 如果老集合没找到,则创建;
- 新集合遍历完了,老集合还有值,则删除。
二、vue中的diff算法(递归)
1,非相同节点,删除,插入新节点
2,相同节点,执行patchVnode()
a,新旧节点完全一样,return
b,新旧节点不一样
i,都有文本节点,但不相同,替换为新的文本节点;
ii,老的有子节点但新的没有,删除
iii,老的没有子节点但新的有,创建
iv,都有子节点,执行updateChildren(),使用首尾指针法,对于子节点,在比较过程中,递归patchVnode()方法
首尾指针法(移动)
新老集合各有连个首尾指针:StartIdx、EndIdx,新老两个变量相互比较:
- oldStartIdx和newStartIdx
- oldEndIdx和newEndIdx
- oldStartIdx和newEndIdx
- oldEndIdx和newStartIdx
在比较过程中,指针往中间走,如果StartIdx>EndIdx则遍历结束。
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) :
1,oldStartVnode和newStartVnode匹配到,两个指针往中间走;
2,oldEndVnode和newEndVnode匹配到,两个指针往中间走;
3,oldStartVnode和newEndVnode匹配到,将oldStartVnode移到当前oldEndVnode后面,两个指针往中间走;
4,oldEndVnode和newStartVnode匹配到,将oldEndVnode移到当前oldStartVnode前面,两个指针往中间走;
5,如果四个比较没有相等时,
a,没有key,直接创建新的节点到当前oldStartVnode之前
b,有key,在老集合查找newStartVnode:
i,查到后,调整真实DOM节点,把newStartVnode放在当前oldStartVnode前面,老位置的index的值为null,newStartIdx后移;
ii,没查到,则在真实DOM中将newStartVnode插入到当前oldStartVnode前面,newStartIdx后移。
遍历结束(创建、删除)
- oldStartIdx > oldEndIdx,新的节点多,多出来的节点需要创建并添加到真实DOM上;
- newStartIdx > newEndIdx,老的节点多,多出来的节点需要从真实DOM中删除。
三、异同
同:
- 都是进行同层级的比较
异:
- Vue 调用 patchVnode,一边比较一边给真实的DOM打补丁
- 当节点元素类型相同但className不同时,vue认为是不同类型的元素,会删除重新创建,而react认为是同类型节点,只会进行修改节点属性
- vue采用首尾指针法,react是从左往右依次对比,比较新老index,oldIndex<newIndex,则移动