最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 初识 React Fiber 设计 | 技术点评

    正文概述 掘金(CAI)   2021-03-13   602

    Fiber 是 React 的一种新的 reconciler 设计,这种新的架构采用 scheduling 来解决之前 stack reconciler 的一些问题以及一些历史遗留问题。React 从 v16.0.0 版本开始使用 fiber reconciler 来渲染和更新组件。

    为什么会有 Fiber

    之前 React 的渲染任务是一个同步任务。同步的任务问题是,当执行该同步任务时,其他任务就需要等待,如果这个任务执行时间过长,就会造成任务阻塞,导致其他任务无法及时执行,比如页面滚动,就会导致页面出现卡顿的现象。

    使用 Fiber 给 React 带来的好处是什么

    1. 可以自己控制渲染过程:
      1. 将渲染任务拆分为任务单元,实现增量渲染。
      2. 任务单元可以暂停、复用和终止。
      3. 可以按任务类型为任务单元设置优先级。
      4. 任务单元可以并发执行。
    2. 优化性能:
      1. 解决掉持续占用主线程的问题之后,优化了页面的体验性能,对一些体验要求较高的场景更适用了,比如动画展示、手势操作。
    3. 顺便解决历史遗留问题:
      1. 方便处理组件异常情况,推出 componentDidCatch 生命周期函数。
      2. 支持 render() 函数返回数组元素,推出 Fragment 组件。

    并且这些优化,对于用户来说基本是无感知的,不会 block 用户的使用。

    Fiber 的实现思路

    渲染一个 React 应用,其实是在调用一个函数,函数本身会调用其他函数,形成调用栈。这种递归调用产生的调用栈我们无法控制。而 Fiber 要做的事情是让函数调用栈可以按需要来运行,可以手动控制。所以 Fiber 可以理解为 reimplementation of the stack。

    React 做了哪些改动

    1. 节点之间是链表关系

    首先 Fiber 节点之间是一个链表关系,这样被设计用来模拟函数调用栈。节点记录了它的父节点、子节点和它的最近的兄弟节点。我们可以拿它和函数调用栈对比一下:

    函数调用栈Fiber
    基本单位函数fiber node输入函数参数props本地状态本地变量state输出函数返回值react element下级调用嵌套函数调用child node上级调用返回地址return node

    如上面的表格所示,fiber node tree 和函数调用栈一样,保存了节点处理的上下文信息,这样我们就可以手动控制节点的渲染过程了。

    2. 为 React Element 创建 Fiber Node

    在 reconciliation 期间,会为 render 方法返回的每个 react element 创建一个 fiber node,对应形成了一棵 fiber node 树,在随后的更新中,React 会重用 fiber 节点,并使用来自 react element 的数据来更新自身的属性,如果从 render 方法返回的 react element 有变化,react 会根据 key 来移动或者删除它。 这其中相当于改变树节点的数据结构,增加了很多属性:

    1. 描述层级关系:
      1. return,指向父节点。
      2. child,指向第一个子节点。
      3. sibling,指向下一个兄弟节点。
    2. stateNode 保存组件的实例。
    3. 副作用种类和副作用链表相关:
      1. effectTag,当需要变化的时候,具体需要执行的操作的类型,比如 Update,Placement。
      2. nextEffect,下一个需要处理的有副作用的 Fiber。
      3. firstEffect 和 lastEffect,本 Fiber 的子树中有副作用的第一个和最后一个 Fiber。
    4. 更新相关的:
      1. updateQueue,更新队列,用于记录状态更新,回调函数,DOM 更新的队列。
      2. memoizedState,上一次更新 fiber 的 state。
      3. memoizedProps,上一次更新 fiber 的 props。
      4. pendingProps,新的 props,将用于子组件或 DOM 元素的 props。
    5. 剩余执行时间相关的:
      1. expirationTime,一个任务单元的可执行时间。
      2. childExpirationTime,用来判断子树是否还有待完成的修改。

    3. Effects 链表

    React 能够非常快速地更新,并且为了实现高性能,它构建了一个有副作用的 fiber 节点的列表,能快速遍历出需要执行修改的节点,比遍历整颗树要快很多,并且不用在没有副作用的节点上花费时间。如下图:

    初识 React Fiber 设计 | 技术点评

    你可以看到带有 effects 的节点是如何链接在一起的,当遍历节点时,React 使用 firstEffect 指针来确定 effects 链表的开始位置。所以上图可以表示为这样的线性链表:

    初识 React Fiber 设计 | 技术点评

    4. 双缓冲

    React 中的双缓冲是指,从旧 fiber 树到新 fiber 树,中间会构建 workInProgress 树,一旦 workInProgress 树构建完成并提交,得到的就是新 fiber 树。这样做的好处是可以减少内存分配和垃圾回收,workInProgress 树的节点不全是新的,比如某颗子树不需要变动,React 会复用这颗子树,减少额外的操作。

    双缓冲技术还有一个好处就是容易处理渲染过程中抛出的异常,当一个节点渲染发生错误时,可以沿用旧 fiber 树的节点,避免整个渲染过程崩掉。

    5. 渲染过程分成了两个阶段

    React fiber 的渲染过程分成 render 阶段和 commit 阶段,相当于之前的 diff 阶段和 patch 阶段。

    render 阶段要完成的工作是不断循环遍历构建 workInProgress 树(构造中的 fiber 节点树),在 fiber 节点上标记 commit 阶段需要完成的工作。commit 阶段则是根据 workInProgress 树上的节点标记,处理 effects 链表,更新真实 DOM 树。一旦这个 workInProgress 树在屏幕上呈现,它就变成了新的 fiber 节点树。

    对于生命周期函数来说,render 阶段会经历的生命周期函数:[UNSAFE_]componentWillMount (即将废弃)、[UNSAFE_]componentWillReceiveProps (即将废弃)、getDerivedStateFromProps、shouldComponentUpdate、[UNSAFE_]componentWillUpdate (即将废弃)、render。commit 阶段会经历的生命周期函数是:getSnapshotBeforeUpdate、componentDidMount、componentDidUpdate、componentWillUnmount。

    前面说到 React 期望手动控制渲染过程,可以暂停、终止和复用渲染过程。对于 render 阶段,可以这么手动控制渲染过程,所以在 render 阶段的生命周期函数有可能会运行多次,所以写代码的时候要注意,避免做有副作用的操作,产生 bug,也因为这样 React 计划废弃其中的一些生命周期函数。对于 commit 阶段则不同,它是一口气把工作做完(同步任务),中间不会暂停,所以尽量不要在这些生命周期里面进行复杂运算。

    为什么 render 阶段可以手动控制渲染过程,而 commit 阶段则需要一次性全部把工作做完。是因为 React 的核心原则之一:一致性,它总是一次性更新 DOM,不会显示部分结果。render 阶段对用户不可见,所以可以一部分一部分的处理渲染任务,而 commit 阶段是把修改更新到 DOM 上,只能一次性更新完,所以需要一口气把所有工作都做完。

    6. 自实现了 window.requestIdleCallback 方法

    window.requestIdleCallback 方法,本身的功能是用来定义当浏览器主线程空闲时要处理的回调函数。fiber 确保不阻塞主线程的关键是在,每次分配一段空闲时间来渲染,时间到了之后,看现在的浏览器主线程是不是空闲的,如果是则继续分配一段空闲时间来渲染,如果不是则停止当前的渲染工作,让出浏览器主线程处理其他任务。所以会预想要用到 window.reqeustIdleCallback 方法。但是由于它是新出的 API,很多浏览器还不支持,且有 issue 有谈到它实际上有点过于严格,并且执行的不够频繁,大概每秒只会执行 20 次,无法保证流畅的 UI 呈现,因此 React 团队决定借鉴它的思想实现了自己的版本,使用 requestAnimationFrame 方法来模拟。但是 requestAnimationFrame 方法有一个缺陷:页面处于后台时回调函数不会执行,因此需要有一个补救措施,使用 setTimeout 设定一个 100ms 的定时时间,去继续执行渲染任务。

    小总结

    本篇是对 React Fiber 设计做了一些简述说明,作为对 React Fiber 设计的初步认识,还没有深入到源码层面,以及改用 Fiber 设计之后,对 React 其他功能的影响和改动,希望以后抽时间把它们写出来,也希望这篇文章能够对你了解 React Fiber 设计有所帮助,有任何评论和建议欢迎在评论区留言讨论~


    封面图片 by Ferenc Almasi on Unsplash


    下载网 » 初识 React Fiber 设计 | 技术点评

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元