1.react中diff算法及列表key属性的作用
React在props或state发生改变时,会调用React的render方法,会创建一颗不同的树。
React需要基于这两颗不同的树之间的差别来判断如何有效的更新UI:
如果一棵树参考另外一棵树进行完全比较更新,那么即使是最先进的算法,该算法的复杂程度为 O(n 3 ),其中 n 是树中元素
的数量;
如果在 React 中使用了该算法,那么展示 1000 个元素所需要执行的计算量将在十亿的量级范围;
这个开销太过昂贵了,React的更新性能会变得非常低效;
于是,React对这个diff算法进行了优化,将其优化成了O(n),如何优化的呢?
同层节点之间相互比较,不会垮节点比较;
不同类型的节点,产生不同的树结构;
开发中,可以通过key来指定哪些节点在不同的渲染下保持稳定;
下面通过三步讲下react的diff算法更新虚拟dom和真实dom机制
情况一:对比不同类型的元素
-
当节点为不同的元素,React会拆卸原有的树,并且建立起新的树:
当一个元素从 a标签 变成 img标签,从 Article 变成 Comment,或从 button标签 变成 div都会触发一个完整的重建
流程;
当卸载一棵树时,对应的DOM节点也会被销毁,组件实例将执行 componentWillUnmount() 方法;
当建立一棵新的树时,对应的 DOM 节点会被创建以及插入到 DOM 中,组件实例将执行 componentWillMount() 方法,
紧接着 componentDidMount() 方法;
比如下面的代码更改:
React 会销毁Top 组件并且重新装载一个新的组件,而不会对Top 进行复用;
情况二:对比同一类型的元素
-
当比对两个相同类型的 React 元素时,React 会保留 DOM 节点,仅比对及更新有改变的属性。
比如下面的代码更改:
通过比对这两个元素,React 知道只需要修改 DOM 元素上的 className 属性;
比如下面的代码更改:
当更新 style 属性时,React 仅更新有所更变的属性。
通过比对这两个元素,React 知道只需要修改 DOM 元素上的 color 样式,无需修改 fontWeight。
如果是同类型的组件元素:
组件会保持不变,React会更新该组件的props,并且调用componentWillReceiveProps() 和 componentWillUpdate() 方
法;
下一步,调用 render() 方法,diff 算法将在之前的结果以及新的结果中进行递归;
情况三:对子节点进行递归
在默认条件下,当递归 DOM 节点的子元素时,React 会同时遍历两个子元素的列表;当产生差异时,生成一个 mutation。
我们来看一下在最后插入一条数据的情况:
前面两个比较是完全相同的,所以不会产生mutation;
最后一个比较,产生一个mutation,将其插入到新的DOM树中即可;
但是如果我们是在中间插入一条数据:
这样react中新树的第一项与旧树的第一项进行比较,不对应,产生一个mutation,以此类推,
React会对每一个子元素产生一个mutation,则是三个mutation,而不是保持first和second的不变;
这种低效的比较方式会带来一定的性能问题;
所以为了解决这种低效的性能问题,可以加入keys的优化
我们在react中遍历列表时,在字元素不加key属性的话总是会提示一个警告,让我们加入一个key属性:
-
方式一:在最后位置插入数据
- 这种情况,有无key意义并不大
-
方式二:在前面插入数据
- 这种做法,在没有key的情况下,所有的li都需要进行修改;
当子元素(这里的li)拥有 key 时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素:
在下面这种场景下,key为111和222的元素仅仅进行位移,不需要进行任何的修改;
将key为333的元素插入到最前面的位置即可;
-
key的注意事项:
key应该是唯一的;
key不要使用随机数(随机数在下一次render时,会重新生成一个数字);
使用index作为key,对性能是没有优化的;
2. vue中diff算法与react中diff算法区别
- vue中diff算法在对新老虚拟daom进行对比时,是从节点的两侧向中间对比,而react是从节点左侧开始对比,就好比将新老虚拟dom放入两个栈中,一对多依次对比;
- vue中在进行节点比较时节点的key值与元素类型相同,属性值不同,就会认为是不同节点,就会删除重建,而react中节点的key值与元素类型相同,属性值不同,react会认为是同类型节点,只是修改节点属性
-
两者的共同点:
都只进行同级比较,忽略了跨级操作;常见的现象就是对数组或者对象中的深层元素进行修改时,视图层监测不到其变化,故不会对dom进行更新,需调用一些特质方法来告诉视图层需要更新dom