React虚拟DOM的 Diff原理全解析
设计思想概述
对一颗树进行更新,如果利用循环递归的方法对每一个节点进行比较,那算法的复杂度可以达到 O(n^3) 简单来说就是一颗有 1000 节点的树,要对比 10亿 次,还不包括比对类型、属性等节点的细节,再好的 CPU 也难以迅速的算出结果
但 React 说它的 Diff 算法能够达到 O(n)
其实它就是偷工减料没有老老实实的对比每一个节点,有一套自己的方法论:
永远只比较同层节点,不会跨层级比较节点
不同的两个节点产生不同的树,把原来的节点以及它的后代全部干掉,换成新的
通过 key 值指定哪些元素是相同的
执行规则 流程
元素类型不相同时
直接将原 VDOM 树上该节点以及该节点下所有的后代节点全部删除,然后替换为新 VDOM 树上同一位置的节点,当然这个节点的后代节点也全都跟着过来了
元素类型相同时
都是 DOM 节点:
通过对比两个元素,React 知道需要修改 DOM 元素上的哪些属性
处理完该节点之后,继续对子节点进行递归
都是组件元素:
组件实例保持不变,更新 props
调用 componentWillReceiveProps() 方法,然后通过 shouldComponentUpdate 返回值决定是否调用 render 方法
处理完该节点之后,继续对子节点进行递归
特殊情况:遍历子元素列表
引入 key 值:
在列表末尾插入一个元素
这样 React 会先匹配两个对应的树,最后插入第三个元素,没有任何问题
但是 如果在头部插入呢 ?
此时前两个元素和原来都不一样,第三个元素被当作新增的节点,明明只需要更新一个节点,现在需要更新三个,这样的情况效率是非常低的
所以,React 引入了 key 值的概念
插入数据之后变成:
现在 React 通过 key 得知 1 和 2 原来是存在的,现在只是换了位置,因此就不需要更新整个节点了,只需要移动位置即可,大大提高效率
选取 key 值的问题
key 选取的原则:不需要全局唯一,但是必须列表中保持唯一
如果使用 数组元素的下标 作为 key 值,在元素顺序不改变的情况下是没有问题的,但一旦顺序发生改变,diff 效率就可能骤然下降 因为在数组中间插入一条数据,那么插入数组之后的数组元素下标都会发生改变,即 key 值改变,那么后面的节点都得更新
不要使用数组下标做 key 值的根本原因在于:数组下标值不稳定,修改顺序就会修改当前 key
最后更新于