Fiber的另类打开方式

笔墨藏久,重拾旧装,继续前行。断更了很久,想着拿起笔写点东西,想了半天不知道该怎么写,感觉可能还是有些地方理解的不够到位,就当是为了记录一下学习的过程,硬着头皮写了一些拙见...

Fiber是什么

FiberReact 中一种用于实现虚拟 DOM 和组件协调的新的架构。它是 React 16 中引入的重要概念,旨在优化渲染过程、实现异步渲染,并提高应用的性能和用户体验。

Fiber 也称协程,它和线程不一样,协程本身是没有并发和并行能力的,它需要配合线程,它只是一种控制流程让出机制。很多时候我们称他为Fiber树,这是因为Fiber既可以看作是链表,也可以看作是树。说他是树,是因为它的形状像树,但是并没有树的特征,树是一个只有指向子节点的指针,并没有指向父节点的指针。

tips:线程是并发执行的基本单位,一个进程可以包含多个线程,每个线程独立执行。而协程则是在单个线程中实现的并发。协程通过协作式的方式在同一个线程中切换执行,可以避免多线程之间的竞争条件和锁的使用,减少了线程之间的切换开销和资源消耗。

其实协程和线程并不一样,协程本身是没有并发或者并行能力的(需要配合线程),因为它只是一种控制流程的让出机制。JavaScript 提供了一种称为生成器 Generator 的功能,它可以用于实现协程。生成器函数可以暂停执行,并且在需要的时候可以从上一个暂停的位置恢复执行。通过调用生成器的 next() 方法,可以逐步执行生成器函数的代码并获取生成的值。此外,还可以使用 yield 关键字来指定生成器函数的暂停点,并将值传递给调用者。

为了更好的理解协程,可以和普通函数一起来看, 以Generator为例:

普通函数执行的过程中无法**被中断和恢复

scss 复制代码
const tasks = []   
function run() {   
let task   
while (task = tasks.shift()) {   
execute(task)  
}   
}  

再来看看 Generator :

scss 复制代码
// 任务列表  
const tasks = []  
function * run () {  
let task  
while(task = task.shift()) {  
// 如果有高优先级的任务  
if (hasHighPriorityTask()) {  
// 中断  
yield  
}  
}  
}  
// 中断后恢复  
const iterator = run()  
// 这样就能恢复了  
iterator.next()  

React Fiber 的思想和协程的概念是契合的: React 渲染的过程可以被中断,可以将控制权交回浏览器,让位给高优先级的任务,浏览器空闲后再恢复渲染

Fiber的结构

Fiber 还可以理解为是一种数据结构,前面说了React Fiber 其实就是一个链表结构。每个 Virtual DOM 都可以表示为一个 fiber,如下图所示:

markdown 复制代码
/**
 * fiber架构
 * type: 标记类型
 * key: 标记当前层级下的唯一性
 * child : 第一个子元素 fiber
 * sibling : 下一个兄弟元素 fiber
 * return: 父fiber
 * node: 真实dom节点
 * props:属性值
 * base: 上次的节点 fiber
 * effectTag: 标记要执行的操作类型(删除、插入、更新)
 */

上面是一个正常的fiber架构里常见的几个属性,除了一些继承下来的属性,下面说几个关键点:

key : 唯一标识fiber节点的属性,帮助react识别哪些节点可以被添加移除或重排

sibling : 下一个兄弟元素 fiber,类似于链表结构,按同级元素一个链接一个,实现同级元素的快速插入、删除

child : 第一个子元素的 fiber,支持向下递归。每个Fiber节点可以有零个或多个子节点,这些子节点是当前节点的直接下级。子节点的处理顺序可以根据算法进行调整,以优化渲染性能。

return : 父 fiber。

每个 fiber 树它可以由多个子 fiber 组成,在首次渲染的时候,会创建 fiberRootrootFiber,fiberRoot 是整个应用的根节点,rootFiber 是组件的根节点,这也就是为什么要求你的组件或者页面的父元素必须是一个单节点。其主要为了方便React进行组件的协调和渲染。如果根元素的直接子元素不止一个节点,React无法确切确定哪个子元素是根节点,从而导致无法正常渲染和更新。

Reactfiber 中多次更新最多会存在两棵 Fiber 树,显示在屏幕上的叫做 current Fiber 树,正在内存构建的是 workInProgress Fiber 树。

当React开始处理更新时,它首先会复制一份当前的Fiber树,这份复制的树就是workInProgress Fiber树。React会在workInProgress Fiber上进行所有的协调和变更,而不是直接在current Fiber上进行操作。这样做的好处是,在处理更新期间,React可以中断和恢复渲染过程,使得应用可以保持响应性。

当所有的协调和变更完成后,React会对比current FiberworkInProgress Fiber之间的差异,以确定需要更新的部分。然后,React将这些更新应用于DOM,将workInProgress Fiber树变为新的current Fiber树。这个过程被称为"提交"。

通过引入workInProgress Fiber树,React能够实现渐进式的渲染。这意味着React可以将渲染工作分成多个步骤,在每个步骤之间进行调度和中断。这有助于提高应用的性能和用户体验,并允许处理大型、复杂的组件树而不会导致阻塞或掉帧。

总结起来就是,current Fiber树是当前显示在屏幕上的Fiber树,而workInProgress Fiber树是正在内存构建和协调的Fiber树。它们的存在使得React能够更好地处理更新,并实现渐进式的渲染。

来点图示,比如在构建的过程中,fiberRoot.current 指向当前界面对应的 fiber 树:

构造完成并渲染, 切换 fiberRoot.current 指针, 使其继续指向当前界面对应的 fiber 树:

整个过程使用深度遍历的方法,对比新老节点,它类似于 diff 算法,判断是否发生了变化以及采取相对应的措施,复用还是变更。

React-reconciler概念

感觉还是得先提出一下这玩意,这个东西是react 得以运行的核心包(综合协调react-dom,react,scheduler各包之间的调用与配合).

管理 react 应用状态的输入和结果的输出. 将输入信号最终转换成输出信号传递给渲染器.

具体的流程大概是这样:

  • 接受输入(scheduleUpdateOnFiber), 将fiber树生成逻辑封装到一个回调函数中(涉及fiber树形结构, fiber.updateQueue队列, 调和算法等),
  • 然后把这个回调函数(performSyncWorkOnRootperformConcurrentWorkOnRoot)送入scheduler进行调度
  • scheduler会控制回调函数执行的时机, 回调函数执行完成后得到全新的 fiber 树
  • 再调用渲染器(如react-dom, react-native等)将 fiber 树形结构最终反映到界面上

scheduler概念

这个东西可以说是fiber里面或者说是react里面最核心的东西之一了,是调度机制的核心实现, 控制由react-reconciler送入的回调函数的执行时机, 在concurrent模式下可以实现任务分片。

大概的流程如下:

  • 核心任务就是执行回调(回调函数由react-reconciler提供)
  • 通过控制回调函数的执行时机, 来达到任务分片的目的, 实现可中断渲染(concurrent模式下才有此特性)

Concurrent模式

什么是Concurrent模式?

这是一个新的特性集合或者说是新的渲染模式。他可以让你的 React 应用保持响应,可以根据用户的设备能力和网络情况优雅地调整。随着 React18 版本的发布, Concurrent 模式成为了 React 的默认模式。它主要分为两个方向的优化,它分别是 CPU密集型I/O密集型

  1. CPU密集型优化:CPU密集型任务是指那些需要进行大量计算或复杂逻辑处理的任务。在传统的同步渲染模式下,当有一个CPU密集型任务在进行时,React会阻塞主线程,导致页面无响应。而在Concurrent模式下,React通过将渲染工作拆分成小的任务单元,并利用浏览器的空闲时间来执行这些任务单元,从而实现任务的并发执行。这样,即使有CPU密集型任务进行,React可以将其任务分散到多个渲染帧中执行,保证了页面的响应性,避免了阻塞。
  2. I/O密集型优化:I/O密集型任务是指那些需要进行大量异步操作或者I/O操作(如网络请求、读写文件等)的任务。在传统的同步渲染模式下,进行I/O操作时,React会等待这些操作完成后再执行渲染工作。这会导致页面卡顿,用户体验下降。而在Concurrent模式下,React可以利用调度器和调度优先级的机制,根据I/O操作的优先级,决定任务的执行顺序。高优先级的I/O任务会被优先执行,这样可以保证对用户交互和数据的快速响应。同时,React还可以利用浏览器的异步API(如requestIdleCallback)来异步执行这些I/O任务,并且通过将多个更新操作合并为一个更新批次,减少对实际DOM的操作次数,从而进一步提升渲染性能。

CPU密集型优化:让位给高优先级

I/O密集型优化:异步渲染+批量更新

总结一下这两个方向的优化就是:更好地管理和调度渲染任务,以适应复杂场景和性能要求较高的应用,同时提高了应用的性能和用户体验。

Concurrent 模式给我们带了什么?

  1. 更好的用户体验:Concurrent 模式使得 React 应用能够更好地响应用户的交互。通过并发渲染,React 可以以更快的速度进行初始渲染,减少首屏加载时间。同时,它还可以将渲染任务分成多个小任务,保持应用的响应性,避免阻塞用户界面。
  2. 更高的性能:由于并发渲染的特点,React 可以更高效地利用 CPU 资源。它可以根据任务的优先级和时间片来动态分配渲染工作,从而提升应用的渲染性能。特别是在处理大型和复杂的应用时,Concurrent 模式能够更有效地渲染组件。
  3. 更好的代码可维护性 :Concurrent 模式引入了新的组件生命周期函数(useDeferredValueuseTransition等),这些函数可以帮助我们优化组件的渲染逻辑和状态管理。通过使用这些新的特性,我们可以更好地组织和管理 React 组件的代码,提高代码的可维护性和可读性。
  4. 更强大的异步渲染能力:Concurrent 模式为 React 提供了更强大的异步渲染能力。通过使用新的异步渲染 API,我们可以更细粒度地控制组件的渲染顺序和优先级。这使得我们能够在处理大量数据或计算密集型任务时,更好地管理渲染的过程。

总结一下,Concurrent 模式为React应用带来了更好的用户体验、更高的性能、更好的代码可维护性和更强大的异步渲染能力。同时为我们开发复杂的应用提供了更强大和灵活的工具和特性。

架构分层

此处分层的标准并非官方说法,只是为了深入理解 react, 在官方标准之外, 对其进行分解和剖析, 方便我们理解 react 架构.

  1. 整个内核部分, 由 3 部分构成:

    1. 调度器
      scheduler包, 核心职责只有 1 个, 就是执行回调.

      • react-reconciler提供的回调函数, 包装到一个任务对象中.
      • 在内部维护一个任务队列, 优先级高的排在最前面.
      • 循环消费任务队列, 直到队列清空.
    2. 构造器
      react-reconciler包, 有 3 个核心职责:

      1. 装载渲染器, 渲染器必须实现HostConfig协议(如: react-dom), 保证在需要的时候, 能够正确调用渲染器的 api, 生成实际节点(如: dom节点).
      2. 接收react-dom包(初次render)和react包(后续更新setState)发起的更新请求.
      3. fiber树的构造过程包装在一个回调函数中, 并将此回调函数传入到scheduler包等待调度.
    3. 渲染器
      react-dom包, 有 2 个核心职责:

      1. 引导react应用的启动(通过ReactDOM.render).
      2. 实现HostConfig协议(源码在 ReactDOMHostConfig.js 中), 能够将react-reconciler包构造出来的fiber树表现出来, 生成 dom 节点(浏览器中), 生成字符串(ssr).

关系图一览: 现将内核 3 个包的主要职责和调用关系, 绘制到一张概览图上:

注意:

  • 红色方块代表入口函数, 绿色方块代表出口函数.
  • package 之间的调用脉络就是通过板块间的入口和出口函数连接起来的.

通过此概览图, 基本可以表述 react 内核层的宏观结构. 后面的内容,基本基于此展开

工作循环

从上面的概览图中,可以看到有两个大的循环, 它们分别位于schedulerreact-reconciler包中:

暂且就将这两个循环分别表述为任务调度循环fiber构造循环,下面对其进行一些简单的了解

  1. 任务调度循环

源码位于Scheduler.js, 它是react应用得以运行的保证, 它需要循环调用, 控制所有任务(task)的调度.

  1. fiber构造循环

源码位于ReactFiberWorkLoop.js, 控制 fiber 树的构造, 整个过程是一个深度优先遍历.

这两个循环对应的 js 源码不同于其他闭包(运行时就是闭包), 其中定义的全局变量, 不仅是该作用域的私有变量, 更用于控制react应用的执行过程.

区别与联系

  1. 区别

    • 任务调度循环是以二叉堆(一种特殊的堆,二叉堆完全二叉树 或者是 近似完全二叉树。)为数据结构, 循环执行的顶点, 直到被清空.
    • 任务调度循环的逻辑偏向宏观, 它调度的是每一个任务(task), 而不关心这个任务具体是干什么的(甚至可以将Scheduler包脱离react使用), 具体任务其实就是执行回调函数performSyncWorkOnRootperformConcurrentWorkOnRoot.
    • fiber构造循环是以为数据结构, 从上至下执行深度优先遍历.
    • fiber构造循环的逻辑偏向具体实现, 它只是任务(task)的一部分(如performSyncWorkOnRoot包括: fiber树的构造, DOM渲染, 调度检测), 只负责fiber树的构造.
  2. 联系

    • fiber构造循环任务调度循环中的任务(task)的一部分. 它们是从属关系, 每个任务都会重新构造一个fiber树.

主干逻辑分析

其实两大循环的分工可以总结为: 大循环(任务调度循环)负责调度task, 小循环(fiber 构造循环)负责实现task .

react 运行的主干逻辑, 即将输入转换为输出的核心步骤, 实际上就是围绕这两大工作循环进行展开.

结合上文的宏观概览图(展示核心包之间的调用关系), 可以将 react 运行的主干逻辑进行概括:

  1. 输入: 将每一次更新(如: 新增, 删除, 修改节点之后)视为一次更新需求(目的是要更新DOM节点).

  2. 注册调度任务: react-reconciler收到更新需求之后, 并不会立即构造fiber树, 而是去调度中心scheduler注册一个新任务task, 即把更新需求转换成一个task.

  3. 执行调度任务(输出): 调度中心scheduler通过任务调度循环来执行task(task的执行过程又回到了react-reconciler包中).

    • fiber构造循环task的实现环节之一, 循环完成之后会构造出最新的 fiber 树.
    • commitRoottask的实现环节之二, 把最新的 fiber 树最终渲染到页面上, task完成.

主干逻辑就是输入到输出这一条链路, 为了更好的性能(如批量更新, 可中断渲染等功能), react在输入到输出的链路上做了很多优化策略, 比如本文讲述的任务调度循环fiber构造循环相互配合就可以实现可中断渲染.

差不多循环和逻辑就是这样,通过这两个大循环概括出react运行的主干逻辑. react-reconcilerScheduler包代码量多且逻辑复杂, 但实际上大部分都是服务于这个主干的。

reconciler 运作流程

在开始讲解 scheduler 内容之前,我们先来了解一下 reconciler 的主要作用,它可以分为以下四个方面:

  1. 输入: 暴露api函数(如: scheduleUpdateOnFiber), 供给其他包(如react包)调用.
  2. 注册调度任务: 与调度中心(scheduler包)交互, 注册调度任务task, 等待任务回调.
  3. 执行任务回调: 在内存中构造出fiber树, 同时与与渲染器(react-dom)交互, 在内存中创建出与fiber对应的DOM节点.
  4. 输出: 与渲染器(react-dom)交互, 渲染DOM节点.

现在将这些功能(从输入到输出)串联起来,reconciler 的整个运作流程可以看做是一个固定的流程,流程用下图表示::

图中的1,2,3,4步骤可以反映react-reconciler从输入到输出的运作流程,这是一个固定流程, 每一次更新都会运行.

这边就大致说一下核心的步骤,如果想详细了解 reconciler的具体细节,以上功能的源码都在ReactFiberWorkLoop.js中.

React 中的优先级管理

基于react@17.0.2, React内部对于优先级的管理, 根据功能的不同分为LanePriority, SchedulerPriority, ReactPriorityLevel3 种类型,稍微展开说一下. 在React 17.0.2中,React内部对于优先级的管理主要分为三种类型:LanePriority(车道优先级)、SchedulerPriority(调度器优先级)和ReactPriorityLevel(React优先级等级)。

  1. LanePriority(车道优先级):也可以称为fiber优先级,车道是React中用于区分更新类型的概念,其位于react-reconciler包。不同类型的更新任务会被分配到不同的车道中,这样可以在处理更新时根据车道的优先级进行区分,以确保高优先级的更新能够优先处理。车道优先级由LanePriority表示。
  2. SchedulerPriority(调度器优先级):调度器是React内部用于安排更新任务执行的模块,其位于scheduler包。调度器优先级用于确定更新任务的调度顺序。具有更高优先级的任务将被优先调度执行,以提高响应性和用户体验。
  3. ReactPriorityLevel(React优先级等级):位于react-reconciler包中的SchedulerWithReactIntegration.js,React还定义了一组优先级等级,用于在不同的React内部模块中进行任务调度和处理。这些等级按照优先级从高到低划分,并包括以下几个级别:ImmediatePriority(立即优先级)、UserBlockingPriority(用户阻塞优先级)、NormalPriority(普通优先级)、LowPriority(低优先级)、IdlePriority(空闲优先级)。

reconciler从输入到输出一共经历了4个阶段(接受输入,协调,渲染,提交), 在每个阶段中都会涉及到与优先级相关的处理. 正是通过对优先级的灵活运用和管理, React实现了可中断渲染,时间切片(time slicing),异步渲染(suspense)等特性,可以更好地控制和安排不同的更新任务,以提供更好的性能和用户体验。它能够确保高优先级的任务得到及时处理,而低优先级的任务则可以在适当的时机被调度执行,从而平衡应用的响应性和资源利用。

React 调度原理(scheduler)

在整个 React 应用中,shceduler 是整个 React 运行时的大心脏,可以说理解了scheduler调度, 就基本把握了 React 的命门。

那么 React 是怎么把任务交给 scheduler 调度和控制执行呢?

先上调度中心最核心的代码: 在SchedulerHostConfig.default.js中.

该 js 文件一共导出了 8 个函数, 最核心的逻辑, 就集中在了这 8 个函数中 :

基于浏览器,简单分析一下:

  1. 调度相关: 请求或取消调度

这 4 个函数源码很简洁, 非常好理解, 它们的目的就是请求执行(或取消)回调函数.重点介绍其中的及时回调

这里可以看出来, 请求回调之后scheduledHostCallback = callback, 然后通过MessageChannel发消息的方式触发performWorkUntilDeadline函数, 最后执行回调scheduledHostCallback.

此处需要注意: MessageChannel在浏览器事件循环中属于宏任务, 所以调度中心永远是异步执行回调函数.

  1. 时间切片(time slicing)相关: 执行时间分割, 让出主线程(把控制权归还浏览器, 浏览器可以处理用户输入, UI 绘制等紧急任务).

注意shouldYieldToHost的判定条件:

  • currentTime >= deadline: 只有时间超过deadline之后才会让出主线程(其中deadline = currentTime + yieldInterval).

    • yieldInterval默认是5ms, 只能通过forceFrameRate函数来修改(事实上在 v17.0.2 源码中, 并没有使用到该函数).
    • 如果一个task运行时间超过5ms, 下一个task执行之前, 会把控制权归还浏览器.
  • navigator.scheduling.isInputPending(): 这 facebook 官方贡献给 Chromium 的 api, 用于判断是否有输入事件(包括: input 框输入事件, 点击事件等).

介绍完这 8 个内部函数, 最后浏览一下完整回调的实现performWorkUntilDeadline(逻辑很清晰, 在注释中解释):

ini 复制代码
const performWorkUntilDeadline = () => {
  if (scheduledHostCallback !== null) {
    const currentTime = getCurrentTime(); // 1. 获取当前时间
    deadline = currentTime + yieldInterval; // 2. 设置deadline
    const hasTimeRemaining = true;
    try {
      // 3. 执行回调, 返回是否有还有剩余任务
      const hasMoreWork = scheduledHostCallback(hasTimeRemaining, currentTime);
      if (!hasMoreWork) {
        // 没有剩余任务, 退出
        isMessageLoopRunning = false;
        scheduledHostCallback = null;
      } else {
        port.postMessage(null); // 有剩余任务, 发起新的调度
      }
    } catch (error) {
      port.postMessage(null); // 如有异常, 重新发起调度
      throw error;
    }
  } else {
    isMessageLoopRunning = false;
  }
  needsPaint = false; // 重置开关
};

给出调度中心的实现图示:

任务队列管理

上面详细说了请求和取消调度的实现原理. 调度的目的是为了消费任务, 接下来就具体分析任务队列是如何管理与实现的: 在Scheduler.js中, 维护了一个taskQueue, 任务队列管理就是围绕这个taskQueue展开.

ini 复制代码
// Tasks are stored on a min heap
var taskQueue = [];
var timerQueue = [];

注意:

  • taskQueue是一个小顶堆数组.
  • 源码中除了taskQueue队列之外还有一个timerQueue队列. 这个队列是预留给延时任务使用的.
  • 其中 timerQueue 表示要延迟执行的任务,taskQueue 表示现在就要执行的任务,这两个数组命名为 Queue,但是它实际上是一个 二叉堆

创建任务

消费任务

创建任务之后, 最后请求调度requestHostCallback(flushWork), flushWork函数作为参数被传入调度中心内核等待回调. requestHostCallback函数在上文调度内核中已经介绍过了, 在调度中心中, 只需下一个事件循环就会执行回调, 最终执行flushWork.

flushWork 函数中,主要做的事情是对几个状态进行修改:

  • isHostCallbackScheduled 设置为 false,表示没有回调函数了;
  • 如果 isHostTimeoutScheduledtrue,将其设置为 false,并取消定时;
  • isPerformingWork 设置为 true,表示任务在执行中了;
  • 调用 workLoop(...) 函数;

workLoop

flushWork已经做了很多它自己能做的事情,他里面调用了workLoop函数去做了一些比如任务中断和任务恢复等事情.其实队列消费的主要逻辑就是在workLoop函数中, 就是前面所说的任务调度循环,我们来看看它的实现是怎么样的.

workLoop就是一个大循环, 虽然代码也不多, 但是非常精髓, 在此处实现了时间切片(time slicing)fiber树的可中断渲染. 这 2 大特性的实现, 都集中于这个while循环.

这个函数的主要执行步骤有以下几个方面:

  • 调用 advanceTimers(...) 函数把过期的任务从 timerQueue(...) 取出来转移到 taskQueue 里执行;
  • 获取优先级最高的任务作为第一个处理的任务;
  • 进入循环,在执行任务前,先看看还有没有时间;
  • 如果没有时间跳出循环,返回 true,标明需要恢复任务;
  • 如果有时间则正常执行任务,并保存任务的返回值;
  • 如果返回值是函数,说明任务执行时长不够了,需要恢复;
  • 如果返回值不是函数,说明已经执行完了,从队列中移除当前任务;
  • 每次调度任务之后,都通过 advanceTimers 把过期的任务从 timerQueue 取出来转移 taskQueue,因为在执行过程中有可能部分任务也过期了;

在上面的代码中调用了 shouldYieldToHost 函数和continuationCallback 函数,前者的主要作用是判断当前时间片是否到期,后者的作用是任务的恢复和执行,不仅完成多个任务的时候会被打断,单个任务执行也会被打断,如果被打断了,那么就返回并等待下次执行。

每一次while循环的退出就是一个时间切片, 深入分析while循环的退出条件:

  1. 队列被完全清空: 这种情况就是很正常的情况, 一气呵成, 没有遇到任何阻碍.

  2. 执行超时: 在消费taskQueue时, 在执行task.callback之前, 都会检测是否超时, 所以超时检测是以task为单位.

    • 如果某个task.callback执行时间太长(如: fiber树很大, 或逻辑很重)也会造成超时
    • 所以在执行task.callback过程中, 也需要一种机制检测是否超时, 如果超时了就立刻暂停task.callback的执行.

时间切片原理

消费任务队列的过程中, 可以消费1~n个 task, 甚至清空整个 queue. 但是在每一次具体执行task.callback之前都要进行超时检测, 如果超时可以立即退出循环并等待下一次调用.

可中断渲染原理

在时间切片的基础之上, 如果单个task.callback执行时间就很长(假设 200ms). 就需要task.callback自己能够检测是否超时, 所以在 fiber 树构造过程中, 每构造完成一个单元, 都会检测一次超时(源码链接), 如遇超时就退出fiber树构造循环, 并返回一个新的回调函数(就是此处的continuationCallback)并等待下一次回调继续未完成的fiber树构造.

总结

也没啥好总结的感觉,调度原理差不多就是这些了,后面还剩下一个fiber树的构造和渲染。

  1. fiber树构造主要讲的是如何对处理好的fiber树进行构造循环处理(初次构建和对比更新)。有兴趣的同学可以看看fiber树构造循环(对应源码位于ReactFiberWorkLoop 函数中)
  2. fiber树渲染其实就是对构造好的fiber树进行进一步处理,作为最后的输出阶段, 是整个reconciler 运作流程的链路中最后一环。主要分为渲染前中后,里面使用到了渲染优先级调度优先级,最终通过渲染器react-dom把最新的 DOM 对象渲染到界面上。 具体的fiber树渲染对应源码,整个逻辑都在commitRoot 函数中

感觉啰嗦了一大堆,讲的主要是一些概念和理解上面的知识,对Fiber的一些知识问答请移步 此处

道阻且长,行则将至...

相关推荐
弓.长.2 分钟前
React Native 鸿蒙跨平台开发:实现商品列表组件
react native·react.js·harmonyos
Dragon Wu8 分钟前
TailWindCss cva+cn管理样式
前端·css
烤麻辣烫13 分钟前
Web开发概述
前端·javascript·css·vue.js·html
Front思24 分钟前
Vue3仿美团实现骑手路线规划
开发语言·前端·javascript
徐同保26 分钟前
Nano Banana AI 绘画创作前端代码(使用claude code编写)
前端
Ulyanov27 分钟前
PyVista与Tkinter桌面级3D可视化应用实战
开发语言·前端·python·3d·信息可视化·tkinter·gui开发
计算机程序设计小李同学27 分钟前
基于Web和Android的漫画阅读平台
java·前端·vue.js·spring boot·后端·uniapp
lkbhua莱克瓦2430 分钟前
HTML与CSS核心概念详解
前端·笔记·html·javaweb
沛沛老爹31 分钟前
从Web到AI:Agent Skills CI/CD流水线集成实战指南
java·前端·人工智能·ci/cd·架构·llama·rag
弓.长.39 分钟前
React Native 鸿蒙跨平台开发:i18n 国际化方案代码指南
react native·react.js·harmonyos