React 架构学习(持续更新...)

100 阅读3分钟

react-reconciler(react 协调器)

有人称之为react 调和过程:整个过程是通过ReactDOM 等库使 VDOMDOM 同步的一个过程。即将虚拟DOM 映射为真实DOM。调和所做的工作包括组件的挂载,卸载,更新等过程。

React 组件更新diff 算法 (前序(根左右)深度优先遍历)

Diff算法的本质是对比新旧VDOM树的变更差异。其核心思想分为三个方面:

  • 同层比较
    • 忽略跨层级操作,如果发现节点不存在,则该节点及其子节点均会被删除。
  • 类型不同,原地替换;类型一致,分层递归
    • 当根节点是不同类型的元素时,会放弃比较,原地替换旧节点
    • 当根节点是相同类型的DOM元素,保留根节点,只更新节点属性
    • 当根节点是相同类型的组件,需要在组件render执行后,根据render返回的VDOM决定如何更新DOM。在比较完根节点后,会以同样的原则递归比较子节点。
  • 通过key重用节点
    • 在列表元素的比较中,如果定义了key,则react根据key匹配子节点,每次渲染后,只要子节点的key不变,则认为是同一个节点,进行复用,提高了更新效率。

react架构的组成可以分为两点:调和器和渲染器

  1. V15版本中的调和器称为栈调和器。
    • stack reconciler栈调和器
      • 调用函数组件或类组件的render方法,将返回的jsx转化为VDOM
      • 对比新老VDOM,即对比VDOM和上次更新时的VDOM,找出本次更新中发生的变化。
      • 通知renderer将变化的VDOM渲染到页面
    • renderer渲染器
      • ReactDOM:浏览器环境渲染
      • ReactNativeapp原生组件渲染
      • ReactTest:渲染出纯js对象用于测试
      • ReactArtcanvas、svg等容器上渲染
    • 主流浏览器每隔16.7ms就刷新一次,而浏览器渲染进程中js线程和GUI渲染线程是互斥的,不能同时执行,所以在每个16.7ms内既要执行js脚本,又要布局和绘制。react 15的更新属于同步更新策略,要递归遍历所有子节点进行diff、更新真实的DOM。并且这个过程是不能被打断的。当组件树的层级很深时,递归调和的时间超过了16.7ms,就会导致用户的交互出现卡顿。
  2. V16版本中的调和器称为纤程调和器(fiber reconciler),新增了调度器调度任务的优先l级,使得高优先级任务优先进入fiber reconciler。核心解决思路:降低视图更新的优先级
    • fiber reconciler会把更新过程进行分片,调度器(scheduler)进行任务分配。当每个片段的任务执行完后就去看看有没有优先级更高的任务去做。如果有,就去把这个高优先级的任务做完,然后重新做更新任务。如果没有,才继续做其它的分片任务。
    • 任务的优先级:
      • synchronous,和stack reconciler一样都是同步执行
      • task:在next tick前执行
      • animation:下一帧前执行
      • high:不久的将来立即执行
      • low:稍微延迟执行
      • offscreen:下一次renderscroll时执行
    • fiber reconciler在执行中分细分为两个阶段:
      • render | reconciliation阶段:生成fiber树,通过diff算法比较更新
      • commit阶段:将需要更新的节点一次性更新完,渲染真实DOM,不能被打断。