blog icon indicating copy to clipboard operation
blog copied to clipboard

浅入 React Fiber 及相关资料整理

Open JTangming opened this issue 6 years ago • 6 comments

fiber 作为一种数据结构,用于代表某些 worker,换句话说,就是一个 work 单元,通过 Fiber 的架构,提供了一种跟踪,调度,暂停和中止工作的便捷方式。

react fiber 及未来

异步渲染 Dan 提出的异步渲染的概念,异步渲染即在以异步的方式加载的同时给人以同步流程的体验,在老设备上,通过牺牲一些加载时间来获得一种流畅的体验。其实在 React@16 版本中,异步渲染默认是关闭的。 生命周期变更 在 react@16 版本中,虽然依旧支持之前的生命周期函数,但是官方已经说明在下个版本中会将废弃其中的部分,这么做的原因,主要是 reconciliation 的重写导致。在 render/reconciliation 的过程中,因为存在优先级和时间片的概念,一个任务很可能执行到一半就被其他优先级更高的任务所替代,或者因为时间原因而被终止。当再次执行这个任务时,是从头开始执行一遍,就会导致组件的某些 xxxwill 生命周期可能被多次调用而影响性能。

react@16 与其说是一个分水岭,不如说是一个过渡,react@17 才会是掀起风浪的那一个。

JTangming avatar Jun 02 '19 12:06 JTangming

司徒正美的 React Fiber架构

JTangming avatar Jul 22 '19 14:07 JTangming

Stack reconciler 自顶向下的递归 diff,这个过程无法中断(持续占用主线程),那么主线程上的layout、动画等周期性任务以及交互响应就无法立即得到处理。

Fiber reconciler 的思路是把渲染/更新过程(递归diff)按照时间分片拆分成一系列小任务,通过任务调度每次检查 fiber tree 上的一小部分,如有空闲时间则继续下一个任务,否则将暂停或终止执行任务,待主线程完成更高优先级任务后再继续。

fiber tree 即 Fiber 上下文的 vDOM tree,更新过程就是根据输入数据以及现有的 fiber tree 构造出新的fiber tree(workInProgress tree)。一些细致一点的概念如下:

DOM
    真实DOM节点
-------
effect
    每个workInProgress tree节点上都有一个effect list
    用来存放diff结果
    当前节点更新完毕会向上merge effect list(queue收集diff结果)
- - - -
workInProgress
    workInProgress tree是reconcile过程中从fiber tree建立的当前进度快照,用于断点恢复
- - - -
fiber
    fiber tree与vDOM tree类似,用来描述增量更新所需的上下文信息
-------
Elements
    描述UI长什么样子(type, props)

JTangming avatar Sep 24 '19 13:09 JTangming

Fiber reconciler

1. render/reconciliation

初次渲染的时候构建 fiber tree,后续state或props有变更,则以此为蓝本,以 fiber node 为工作单元,自顶向下逐节点构造 workInProgress tree(构建中的新fiber tree)

具体过程如下:

  • 如果当前节点不需要更新,直接把子节点 clone 过来,跳到5,要更新的话打个 tag
  • 更新当前节点状态(props, state, context等)
  • 调用 shouldComponentUpdate,如若返回 false 则跳到5
  • 调用 render() 获得新的子节点,并为子节点创建 fiber(创建过程会尽量复用现有 fiber,子节点增删也发生在这里)
  • [标识位5] 如果没有 child fiber,该工作单元结束,把 effect list 归并 return,并把当前节点的 sibling 作为下一个工作单元,否则把 child 作为下一个工作单元
  • 在工作单元工作结束,则看看任务调度有无更高优先级任务,否则继续
  • 如果没有下一个工作单元了,则回到了 workInProgress tree 的根节点,第1阶段结束,进入pendingCommit状态

workloop 结束时,workInProgress tree 的根节点身上的 effect list 就是收集到的所有 side effect(因为每做完一个都向上归并),所以,构建 workInProgress tree 的过程就是 diff 的过程。

workInProgress tree 构造完毕,得到的就是新的 fiber tree,然后把 current 指针指向 workInProgress tree,丢掉旧的 fiber tree。这样做的好处:能够复用内部对象(fiber),节省内存分配、GC 的时间开销。

2. commit(该阶段不能打断)

  • 处理 effect list,包括 3 种处理:更新DOM树、调用组件生命周期函数以及更新ref等内部状态
  • 把所有的 DOM changes 的 patches 都 commit 到 DOM 树上

任务调度

diff 的粒度控制为一个fiber node,实际上就是按虚拟 DOM 节点拆,因为 fiber tree 是根据 v-DOM tree 构造出来的,树结构一模一样,只是节点数据信息有差异。

任务调度分两部分:工作循环 & 优先级机制。

工作循环是基本的任务调度机制,工作循环中每次处理一个任务(工作单元),处理完毕有一次喘息的机会,基本规则是:每个工作单元结束检查是否还有时间做下一个,没时间了就先“挂起”

优先级机制用来处理突发事件与优化次序。

中断/断点恢复

  • 中断:检查当前正在处理的工作单元,保存当前成果(firstEffect, lastEffect),修改 tag 标记一下,迅速收尾并再开一个requestIdleCallback,下次有机会再做
  • 断点恢复:下次再处理到该工作单元时,看tag是被打断的任务,接着做未完成的部分或者重做

JTangming avatar Sep 24 '19 13:09 JTangming

todo:

  • React Fiber Side Effects
  • [React Fiber单向链表架构](The how and why on React’s usage of linked list in Fiber to walk the component’s tree)

JTangming avatar Sep 24 '19 13:09 JTangming