新旧虚拟DOM应该如何比对呢?
react底层使用Diff(difference)算法来进行比对。我们知道,调用了setState方法时,数据(state,props)发生改变,就会进行比对。
下面我们先来看看setState方法。
setState方法是异步的,这样能够很好地提升react性能。假如我们要连续调用三次setState,变更三组数据,那么如果做三次虚拟DOM比对,更新三次页面。如果这三次调用的时间间隔非常短,这样是非常浪费性能的;react不会这样,它会把三个setState合并成一个,只做一次虚拟DOM的比对,更新一次页面。
我们再来看看Diff算法
Diff算法有个很重要的概念:同级比较。
首先会比较最顶层的虚拟DOM节点是否一致,如果一致的话,就继续比较下一层的节点;如果不一致的话,react就会把这个节点及其下面所有节点全部删掉,重新生成一遍新的DOM,然后用新的DOM替换原始页面的DOM。有人可能会说,这样性能不是会很低吗?如果除了这个最顶层节点不一致,下面都是一致的,这样整个替换,不会很浪费性能吗?其实,虽然可能会造成DOM重新渲染性能上的浪费,但比对的算法简单,由此比对速度也会很快,大大减少了比对算法上的性能消耗,所以采用同级比较的算法。
假如我们一个数组,数组中有5个数据,在页面第一次渲染的时候,我们把这5个数据映射成5个虚拟DOM节点,生成一颗小的虚拟DOM树,如上图框框处。接着,我们又往数组里面增加了一个数据,由于数据发生变化,所以生成了一个新的虚拟DOM树。再接着进行两棵树的比对,如果每个虚拟DOM节点没有key值的话,在比对的过程中很难确定节点与节点的关系;如果有key值,就相当于给每个节点起了一个名字,如图右边,虚拟DOM节点根据key值作关联,将key值相同即名字相同的节点进行比较,这样极大的提升了react性能。
通常不建议用index作为key值,因为这样会导致key值不稳定,如一个数组有3个元素,将index值作为key值
现在我们删除元素a
从上面可以看出,刚开始,b的key值是1,删除a后,b的key值变成了0,现在的b就无法与之前的b建立起关系。用index作为key值,导致key值不稳定,就失去了key值的意义。