Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

浅入 React Fiber 及相关资料整理 #16

Open
JTangming opened this issue Jun 2, 2019 · 6 comments
Open

浅入 React Fiber 及相关资料整理 #16

JTangming opened this issue Jun 2, 2019 · 6 comments

Comments

@JTangming
Copy link
Owner

JTangming commented Jun 2, 2019

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

react fiber 及未来

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

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

@JTangming
Copy link
Owner Author

@JTangming
Copy link
Owner Author

React Fiber 架构学习

@JTangming
Copy link
Owner Author

司徒正美的 React Fiber架构

@JTangming
Copy link
Owner Author

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
Copy link
Owner Author

JTangming commented Sep 24, 2019

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
Copy link
Owner Author

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 JTangming changed the title React fiber React Fiber Feb 21, 2020
@JTangming JTangming changed the title React Fiber 浅入 React Fiber 及相关资料整理 Feb 21, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant