x-note
  • Introduction
  • JavaScript
    • JavaScript 作用域链
    • JavaScript 数据结构与类型
    • JavaScript 原型
    • JavaScript this 关键字
    • JavaScript 函数
    • JavaScript delete 运算符
    • JavaScript 内存管理与垃圾回收
    • JavaScript 严格模式与混乱模式
    • JavaScript 数字精度丢失
    • JavaScript 并发模型
    • 利用原型链实现继承
  • ECMAScript
    • ECMAScript 6 变量及常量的声明
    • ECMAScript 6 变量的解构赋值
    • ECMAScript 6 Promise 对象
    • ECMAScript 6 Symbol
    • ECMAScript 6 Proxy
    • ECMAScript 6 Reflect
    • ECMAScript 6 new.target
    • ECMAScript 6 Set 和 WeakSet
    • ECMAScript 6 Map 和 WeakMap
    • ECMAScript 6 Iterator
    • ECMAScript 6 Generator
    • ECMAScript 6 class
    • ECMAScript 7
    • ECMAScript 8 async 函数
    • ECMAScript 8 内存共享与原子性
    • ECMAScript 8 Others
    • ECMAScript 2018
    • ECMAScript 2019
  • CSS
    • CSS 块格式化上下文(BFC)
    • CSS 盒模型
    • CSS 外边距合并
    • CSS Float
    • CSS Position
    • CSS Border-Image
    • CSS BEM
    • CSS 表布局详解
    • 页面布局之单列布局
    • 页面布局之多列布局
  • React
    • React 组件的生命周期
    • React 虚拟 DOM
    • React Reconciliation
    • React Diff 算法核心
    • React Fiber
    • React Scheduling
    • React Context API
    • React Refs
    • React HMR
    • React Hook
  • VUE
    • VUE 响应式系统
    • VUE 渲染机制
    • 关于 Vue 的思考
  • Webpack
    • Webpack 基本概念
    • Webpack HMR
  • Babel
    • @babel/preset-env
  • WEB
    • WEB 基础知识及概念
      • 屏幕测量单位
      • 重绘与重排
      • 前端模块化系统
      • WEB 客户端存储
      • 浏览器的渲染过程
    • WEB 性能优化
      • WEB 性能指标
      • WEB 图片优化
      • 懒加载资源
    • WEB 安全
      • XSS
      • XSRF
      • 点击劫持
      • 同源策略(Same Origin Policy,SOP)
    • WEB 解决方案
      • webp 兼容方案
      • WEB 拖拽实现方案
    • WEB SEO
  • Git
    • Git 工作流
    • Git 内部原理
  • 传输协议
    • UDP
      • UDP 基本概念
    • TCP
      • TCP 基本概念
    • HTTP
      • HTTP 基础
      • HTTP 缓存
      • HTTP-2
      • HTTP-3
      • HTTPS
      • 自定义 HTTPS 证书
  • Protocol Buffers
    • Protocol Buffers 基础
  • gRPC
    • gRPC 简介
    • gRPC 基础概念
    • GRPC with GraphQL and TypeScript
  • 正则表达式
    • 正则表达式基础
    • 正则表达式的悲观回溯
  • 基础算法
    • 冒泡排序
    • 插入排序
    • 选择排序
    • 快速排序
    • 归并排序
    • 希尔排序
    • 堆排序
    • 桶排序
    • 计数排序
    • 基数排序
    • 二叉树的遍历
    • 动态规划
    • 回溯
  • 压缩算法
    • HPACK
    • QPACK
  • 设计模式
    • DDD
      • 模型元素的模式
    • 常见设计模式
      • 工厂方法
      • 抽象工厂
      • 构造器
      • 原型
      • 单例模式
      • 适配器模式
      • 桥接模式
      • 组合模式
      • 外观模式
      • 享元模式
      • 代理模式
      • 责任链模式
      • 命令模式
      • 迭代器模式
      • 中介者模式
      • 备忘录模式
      • 观察者模式
      • 状态模式
      • 策略模式
      • 模版方法模式
      • 访问者模式
      • 依赖注入
    • MVC
    • MVP
    • MVVM
  • 颜色空间
    • LCH
由 GitBook 提供支持
在本页
  • Scheduling
  • Fiber
  • Fiber 的结构
  • Todo In Feature
  • 参考
在GitHub上编辑
  1. React

React Fiber

React Fiber 是对 React 核心算法的不断重新实现,目标是提高 React 在动画,布局和手势等领域的适用性。

React Fiber 的功能包括:

  • 支持增量渲染,将渲染工作分成多个块并将其分布到多个帧中的能力(主要功能);

  • 当出现新的更新时,支持暂停,中止和复用;

  • 支持为不同类型的更新分配优先级;

  • 新的并发原函数(primitives)。

Scheduling

scheduling -- 确定何时应执行 work 的过程。 work -- 必须执行的任何计算. Work 通常是更新 (e.g. setState) 的结果.

React 的设计原则文档中提到:

  • 在用户界面中,无需立即应用每个更新。实际上,这样做可能是浪费的,导致帧下降并降低用户体验。

  • 不同类型的更新具有不同的优先级-动画更新需要比数据存储中的更新更快地完成。

  • 基于推送的方法要求应用程序(您,程序员)决定如何安排工作。基于拉的方法使框架(React)变得智能,并为您做出那些决定。

修改 React 的核心算法以利用 Scheduling 是 Fiber 背后的驱动思想。

Fiber

已经确定,Fiber 的主要目标是使 React 能够利用 scheduling 的优势。具体来说,需要能够

  • 暂停 work,稍后再回来

  • 为不同类型的 work 分配优先级

  • 重用以前完成的 work

  • 中止 work,如果不再需要

为了做到这一点,首先需要一种将 work 分解为单元的方法。从某种意义上讲 Fiber 代表 work 的单元。

首先了解 React 组件作为数据函数的概念

React 的核心前提是 UI 只是 data 到 data 的投影。相同的输入给出相同的输出。一个简单的纯函数。

v = fn(d);

因此,呈现 React 应用程序类似于调用一个函数,该函数的主体包含对其他函数的调用,并依此类推。当考虑 Fiber 时,这种类比很有用。

计算机通常使用调用堆栈来跟踪程序执行的方式。 执行函数时,新的堆栈帧将添加到堆栈中。 该堆栈帧表示该函数执行的工作。

如果一次执行太多 work,可能会导致动画掉帧并显得断断续续。而且,如果最新的更新取代了某些 work,则这些 work 可能是不必要的。 这是 UI 组件和 function 之间的比较失败的地方,因为与一般 function 相比,组件具有更多特定的关注点。

较新的浏览器(和 React Native)实现了有助于解决此确切问题的 API:requestIdleCallback 安排在空闲期间调用的低优先级函数,而 requestAnimationFrame 安排在下一个动画帧上调用的高优先级函数。

为了使用这些 API,您需要一种将渲染工作分解为增量单位的方法。 如果仅依赖调用堆栈,它将继续工作直到堆栈为空。

如果可以自定义调用堆栈的行为来优化呈现 UI,那不是很好吗???? 如果可以随意中断调用堆栈并手动操作堆栈帧,那不是很好吗????

这就是 React Fiber 的目的。Fiber 是堆栈的重新实现,专门用于 React 组件。可以将单个 Fiber 视为虚拟堆栈帧。

重新实现堆栈的优点是,可以将堆栈帧保留在内存中,并根据需要(以及在任何时候)执行它们。

除了 Scheduling 之外,手动处理堆栈帧还可以释放并发和错误边界等功能。

Fiber 的结构

注意,随着 React 的发展,Fiber 的结构可能会存在变化

具体来说,Fiber 是一个 JavaScript 对象,其中包含有关组件,其输入和其输出的信息。

Fiber 相当于虚拟堆栈的帧,也相当于组件的实例。

interface FiberProperty = {
  type: string | () => {};
  key: string;
  child: Fiber;
  sibling: Fiber;
  return: Fiber;
  pendingProps: Object;
  memoizedProps: Object;
  pendingWorkPriority: Number;
  alternate: 'flush' | 'work-in-progress';
  output: HostComponent;
};

class Fiber<P = {}> extends FiberProperty<P> {}

type 和 key

Fiber 的 type 和 key 的作用与 React Element 的作用相同。(实际上,当从 Element 创建 Fiber 时,这两个字段将直接复制。)

Fiber 的 type 描述了它所对应的组件。对于复合组件,type 是函数或类组件本身。对于基本组件(eg:div,span),类型为字符串。

从概念上讲,type 是一个函数(如 v = f(d)),其执行由堆栈帧跟踪

与 type 一起,key 在 reconciliation 期间用于确定 Fiber 是否可以重复使用。

child 和 sibling

这两个字段指向其它的 Fiber,以描述 Fiber 树。

子 Fiber 对应于组件的 render 方法返回的值。

对于以下例子:

function Parent() {
  return <Child />;
}

Parent 的子 fiber 相当于 Child

字段 sibling 说明了渲染返回多个子项的情况

function Parent() {
  return [<Child1 />, <Child2 />];
}

子 Fiber 形成一个单链列表,其头是第一个子链。 对于上述示例,Parent 的 child 指向 Child1,Child1 的 sibling 指向 Child2

回到函数类比,可以将子 Fiber 视为尾调用函数(tail-called function)。

return

return fiber 是程序在处理完当前 Fiber 之后应返回的 Fiber。 从概念上讲,它与堆栈帧的返回地址相同。 也可以将其视为父 Fiber。

如果 Fiber 具有多个子 Fiber,则每个子 Fiber 的 return fiber 都是父 Fiber。因此,在之前的示例中,Child1 和 Child2 的返回 Fiber 为 Parent。

pendingProps and memoizedProps

从概念上讲,props 是函数的参数。Fiber 的 pendingProps 在执行开始时设置,memoizedProps 在结束时设置。

当传入的 pendingProps 等于 memoizedProps 时,它表示可以复用 Fiber 的先前输出,从而避免了不必要的工作。

pendingWorkPriority

一个数字,指示 Fiber 代表的 work 优先级。 ReactPriorityLevel 模块列出了不同的优先级及其代表的含义。

除 NoWork 为 0 外,数字越大表示优先级越低。例如,您可以使用以下功能来检查 Fiber 的优先级是否至少与给定级别一样高:

// 此 function 仅用于说明;它不是 React Fiber 代码库的一部分。
function matchesPriority(fiber, priority) {
  return (
    fiber.pendingWorkPriority !== 0 && fiber.pendingWorkPriority <= priority
  );
}

调度程序使用优先级字段来搜索要执行的下一个 work 单元。

alternate

  • flush -- 将 Fiber 输出渲染到屏幕上

  • work-in-progress -- 尚未完成的 Fiber;从概念上讲,尚未返回的堆栈帧。

在任何时候,一个组件实例最多具有两个与其对应的 Fiber: current, flushed fiber 和 work-in-progress fiber。

current Fiber 的 alternate 是 work-in-progress ,而 work-in-progress 的 alternate 是 current Fiber。

使用称为 cloneFiber 的函数延迟创建 Fiber 的 alternate。并非总是创建一个新的对象,而是 cloneFiber 将尝试复用 Fiber 的 alternate(如果存在),以最大程度地减少分配。

应该将 alternate 字段视为实现细节,但是它经常在代码库中弹出,因此进行讨论很有价值。

output

host component React 应用程序的叶节点。它们特定于渲染环境(例如,在浏览器应用中,它们是div,span等)。在 JSX 中,它们使用小写标记名称表示。

从概念上讲,Fiber 的输出是函数的返回值。 正如之前所一直强调的。

每个 Fiber 最终都有输出,但是输出仅由 host compoennt 在叶节点上创建。然后将输出传送到树上。

输出是最终提供给渲染器的输出,以便可以将更改刷新到渲染环境。定义输出的创建和更新方式是渲染器的责任。

Todo In Feature

  • 调度程序如何找到要执行的下一个工作单元。

  • 如何通过光纤树跟踪和传播优先级。

  • 调度程序如何知道何时暂停和继续工作。

  • 如何刷新工作并将其标记为完成。

  • 副作用(例如生命周期方法)如何工作。

  • 协程是什么,以及如何用于实现上下文和布局等功能。

参考

上一页React Diff 算法核心下一页React Scheduling

最后更新于1年前

React Fiber Architecture
React Reconciler
React Design Principles
React Basic Theoretical Concepts
What's Next for React (ReactNext 2016)