• React之diff原理


    一、是什么

    Vue一致,React通过引入Virtual DOM的概念,极大地避免无效的Dom操作,使我们的页面的构建效率提到了极大的提升

    diff算法就是更高效地通过对比新旧Virtual DOM来找出真正的Dom变化之处

    传统diff算法通过循环递归对节点进行依次对比,效率低下,算法复杂度达到 O(n^3),react将算法进行一个优化,复杂度姜维O(n),两者效率差距如下图:

    二、原理

    reactdiff算法主要遵循三个层级的策略:

    • tree层级

    • conponent 层级

    • element 层级

    tree层级

    DOM节点跨层级的操作不做优化,只会对相同层级的节点进行比较

    只有删除、创建操作,没有移动操作,如下图:

    react发现新树中,R节点下没有了A,那么直接删除A,在D节点下创建A以及下属节点

    上述操作中,只有删除和创建操作

    conponent层级

    如果是同一个类的组件,则会继续往下diff运算,如果不是一个类的组件,那么直接删除这个组件下的所有子节点,创建新的

    component D换成了component G 后,即使两者的结构非常类似,也会将D删除再重新创建G

    element层级

    对于比较同一层级的节点们,每个节点在对应的层级用唯一的key作为标识

    提供了 3 种节点操作,分别为 INSERT_MARKUP(插入)、MOVE_EXISTING (移动)和 REMOVE_NODE (删除)

    如下场景:

    通过key可以准确地发现新旧集合中的节点都是相同的节点,因此无需进行节点删除和创建,只需要将旧集合中节点的位置进行移动,更新为新集合中节点的位置

    流程如下表:

    • index: 新集合的遍历下标。
    • oldIndex:当前节点在老集合中的下标
    • maxIndex:在新集合访问过的节点中,其在老集合的最大下标

    如果当前节点在新集合中的位置比老集合中的位置靠前的话,是不会影响后续节点操作的,这里这时候被动字节不用动

    操作过程中只比较oldIndex和maxIndex,规则如下:

    • 当oldIndex>maxIndex时,将oldIndex的值赋值给maxIndex
    • 当oldIndex=maxIndex时,不操作
    • 当oldIndex

    diff过程如下:

    • 节点B:此时 maxIndex=0,oldIndex=1;满足 maxIndex< oldIndex,因此B节点不动,此时maxIndex= Math.max(oldIndex, maxIndex),就是1
    • 节点A:此时maxIndex=1,oldIndex=0;不满足maxIndex< oldIndex,因此A节点进行移动操作,此时maxIndex= Math.max(oldIndex, maxIndex),还是1
    • 节点D:此时maxIndex=1, oldIndex=3;满足maxIndex< oldIndex,因此D节点不动,此时maxIndex= Math.max(oldIndex, maxIndex),就是3
    • 节点C:此时maxIndex=3,oldIndex=2;不满足maxIndex< oldIndex,因此C节点进行移动操作,当前已经比较完了

    当ABCD节点比较完成后,diff过程还没完,还会整体遍历老集合中节点,看有没有没用到的节点,有的话,就删除

    三、注意事项

    对于简单列表渲染而言,不使用key比使用key的性能,例如:

    将一个[1,2,3,4,5],渲染成如下的样子:

    1. <div>1</div>
    2. <div>2</div>
    3. <div>3</div>
    4. <div>4</div>
    5. <div>5</div>

    后续更改成[1,3,2,5,4],使用key与不使用key作用如下:

    1. 1.加key
    2. <div key='1'>1</div> <div key='1'>1</div>
    3. <div key='2'>2</div> <div key='3'>3</div>
    4. <div key='3'>3</div> ========> <div key='2'>2</div>
    5. <div key='4'>4</div> <div key='5'>5</div>
    6. <div key='5'>5</div> <div key='4'>4</div>
    7. 操作:节点2移动至下标为2的位置,节点4移动至下标为4的位置。
    8. 2.不加key
    9. <div>1</div> <div>1</div>
    10. <div>2</div> <div>3</div>
    11. <div>3</div> ========> <div>2</div>
    12. <div>4</div> <div>5</div>
    13. <div>5</div> <div>4</div>
    14. 操作:修改第1个到第5个节点的innerText

    如果我们对这个集合进行增删的操作改成[1,3,2,5,6]

    1. 1.加key
    2. <div key='1'>1</div> <div key='1'>1</div>
    3. <div key='2'>2</div> <div key='3'>3</div>
    4. <div key='3'>3</div> ========> <div key='2'>2</div>
    5. <div key='4'>4</div> <div key='5'>5</div>
    6. <div key='5'>5</div> <div key='6'>6</div>
    7. 操作:节点2移动至下标为2的位置,新增节点6至下标为4的位置,删除节点4
    8. 2.不加key
    9. <div>1</div> <div>1</div>
    10. <div>2</div> <div>3</div>
    11. <div>3</div> ========> <div>2</div>
    12. <div>4</div> <div>5</div>
    13. <div>5</div> <div>6</div>
    14. 操作:修改第1个到第5个节点的innerText

    由于dom节点的移动操作开销是比较昂贵的,没有key的情况下要比有key的性能更好

  • 相关阅读:
    spring boot基于Java的电影院售票与管理系统毕业设计源码011449
    python - 闭包与装饰器
    在项目中应用设计模式的实践指南
    app商品详情原数据 API ——淘宝/天猫
    jdk8u201版本cpu.load过高问题的排查和解决
    uniapp项目,使用HbuilderX建立eslint规范
    ISPRS2021/云检测:一种地理信息驱动的方法和一种新的大规模遥感云/雪检测数据集
    Spring Boot 集成MyBatis-Plus
    让图片说话SadTalker
    java-php-python-ssm养老院信息管理计算机毕业设计
  • 原文地址:https://blog.csdn.net/Ming_xm/article/details/134008337