React的diff算法基于两个假设:
1、不同元素的类型会产生不同的树
2、开发者可以通过key prop标识一个元素在不同的渲染下可以保持稳定(及相同key的元素不需要更新整个元素,不同的key需要更新整个元素)
基于上述假设,React认为在视图更新前后,如果两个节点的Tag名(对于原生标签)或者组件(对于自定义组件)相同,则它们是同一个节点,而且如果是不同的根节点,那么子节点也不需要对比,直接用新树替换掉旧的树即可。
对比的过程
1、当一个组件触发更新(setState),则React会diff以这个组件为根节点的整个虚拟DOM树,对比更新后的虚拟DOM和更新前的虚拟DOM。
2、如果两个节点不同,则用新的节点替换掉旧的节点;如果两个节点相同(假设为oldNode和newNode),则对比它们的属性、innerText和子节点。
3、如果oldNode有某个子节点someOldChild,而newNode没有这个子节点(即newNode没有一个子节点,和someOldNode有相同的tag名、自定义组件引用或者key值),则删掉someOldChild;如果newNode有某个子节点somNewChild,而oldNode没有,则添加someNewChild;如果oldNode和newNode都有某个节点someChild,则将其移动到正确的位置,并递归地进行对比工作,即以someChild为根节点对比新旧两棵虚拟DOM树。
从上面的对比过程可以看出,React的diff(其实Vue也是一样)并不会考虑到节点跨层移动的情况,因此有些观点认为React的对比过程可以描述为“按层比较”(level by level)。