react-reconciler(react 协调器)
有人称之为react 调和过程:整个过程是通过ReactDOM 等库使 VDOM 和 DOM 同步的一个过程。即将虚拟DOM 映射为真实DOM。调和所做的工作包括组件的挂载,卸载,更新等过程。
React 组件更新diff 算法 (前序(根左右)深度优先遍历)
Diff算法的本质是对比新旧VDOM树的变更差异。其核心思想分为三个方面:
- 同层比较
- 忽略跨层级操作,如果发现节点不存在,则该节点及其子节点均会被删除。
- 类型不同,原地替换;类型一致,分层递归
- 当根节点是不同类型的元素时,会放弃比较,原地替换旧节点
- 当根节点是相同类型的
DOM元素,保留根节点,只更新节点属性 - 当根节点是相同类型的组件,需要在组件
render执行后,根据render返回的VDOM决定如何更新DOM。在比较完根节点后,会以同样的原则递归比较子节点。
- 通过
key重用节点- 在列表元素的比较中,如果定义了
key,则react根据key匹配子节点,每次渲染后,只要子节点的key不变,则认为是同一个节点,进行复用,提高了更新效率。
- 在列表元素的比较中,如果定义了
react架构的组成可以分为两点:调和器和渲染器
- 在
V15版本中的调和器称为栈调和器。stack reconciler栈调和器- 调用函数组件或类组件的
render方法,将返回的jsx转化为VDOM。 - 对比新老VDOM,即对比
VDOM和上次更新时的VDOM,找出本次更新中发生的变化。 - 通知
renderer将变化的VDOM渲染到页面
- 调用函数组件或类组件的
renderer渲染器ReactDOM:浏览器环境渲染ReactNative:app原生组件渲染ReactTest:渲染出纯js对象用于测试ReactArt:canvas、svg等容器上渲染
- 主流浏览器每隔
16.7ms就刷新一次,而浏览器渲染进程中js线程和GUI渲染线程是互斥的,不能同时执行,所以在每个16.7ms内既要执行js脚本,又要布局和绘制。react 15的更新属于同步更新策略,要递归遍历所有子节点进行diff、更新真实的DOM。并且这个过程是不能被打断的。当组件树的层级很深时,递归调和的时间超过了16.7ms,就会导致用户的交互出现卡顿。
- 在
V16版本中的调和器称为纤程调和器(fiber reconciler),新增了调度器调度任务的优先l级,使得高优先级任务优先进入fiber reconciler。核心解决思路:降低视图更新的优先级。fiber reconciler会把更新过程进行分片,调度器(scheduler)进行任务分配。当每个片段的任务执行完后就去看看有没有优先级更高的任务去做。如果有,就去把这个高优先级的任务做完,然后重新做更新任务。如果没有,才继续做其它的分片任务。- 任务的优先级:
synchronous,和stack reconciler一样都是同步执行task:在next tick前执行animation:下一帧前执行high:不久的将来立即执行low:稍微延迟执行offscreen:下一次render或scroll时执行
fiber reconciler在执行中分细分为两个阶段:render | reconciliation阶段:生成fiber树,通过diff算法比较更新commit阶段:将需要更新的节点一次性更新完,渲染真实DOM,不能被打断。