react diff

248 阅读2分钟

执行时间: render生成element树形数据之后,渲染dom element之前。

优势: 算法复杂度为 O(n),传统diff算法复杂度为O(n^3)。

基于假设:

  1. 两个不同类型的element会产出不同的树;
  2. 开发者可以通过设置 key 属性,来告诉react,哪些子元素可以不变。

对比形式:

1.不同类型的element

   什么是不同类型的element?比如,div → a; img → span; 等等。

   例如div→ a,这里会把原来的节点div和其子节点卸掉,并渲染新的节点a。

   对于div的示例,会执行componentWillUnmount迎来生命周期的结束。对于组件a,则会在实例化之后,执行UNSAFE_componentWillMount 和 componentDidMount,完成渲染。

2.相同类型的dom element

例如,div className='a' → div className='b; div style={{width: 20px, height 30px}} → div style = {{width: 20px, height: 40px}};

    dom节点保持一样,只做属性的对比。

        对于div className='a' → div className='b这种情况,react只更新div dom的class。

        对于div style={{width: 20px, height 30px}} → div style = {{width: 20px, height: 40px}};这种情况,react只会更改style里的height。

3.相同类型的component element

    什么是相同类型的component element?比如,有个组件叫Cmp, 它要更新了。

    这种组件在更新的时候,实例是不变的,react将通过修改组件实例的props,以保element和新的element是一致的,并调用实例的UNSAFE_componentWillReceiveProps,UNSAFE_componentWillUpdate,componentDidUpdate方法。

对比过程:

对子节点递归

    react只对相同层级的子节点递归对比。而且大部分会穿插以上三种对比形式。比如最常见的列表。

<ul className='ul0'>
  <li>first</li>
  <li>second</li>
</ul>

<ul className='ul1'>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

如果单纯的从ul0变化到ul1,react在处理element的时候,对于li的元素,会直接走不同类型的element。 对比之后,直接将third 插入。

对于ul来讲,会走相同类型的dom element。 对比之后,修改ul的className属性

如果你的li是一个自定义的Component,那就会涉及到相同类型的component element。

以上是添加元素的例子,似乎并没有什么额外的开销,如果想在first之前插入 zero,试想react会做什么?

对,react会把0,1,2号li全部重新创建一遍,其中还涉及到原来1,2元素的卸载。

所以,有了Key。

key

Key的出现,让开发者可以给element一个特殊的标识,如果key不变,说明这个element,还是之前的element,只是移动了顺序。如果变了,那就新建。

所以,如果在react中用数组的index作为key,需要保证,数组不会重新排序。如果重新排序了,状态就会拿之前的,导致排序失效。这也是为什么,一个数组map不传key,react会给warning的原因。

可以感受一下:

官方例子:codepen.io/pen?&editor…