第一章 React理念

前言

这个系列是React技术揭秘的读书笔记,用于了解React源码。

主要参考资料还有React的官方Repo

理念篇

第一章 React理念

React 理念

快速响应是React要解决的核心问题,包括两个瓶颈:CPU瓶颈IO瓶颈。而在源码内部,为了支持这些特性,同样需要将同步的更新变为可中断的异步更新

React的特点是:声明式(declarative,不同于命令式imperative,二者区别)、组件化(Component-Based)、跨平台。

CPU瓶颈

当预留的时间不够用时,React将线程控制权交还给浏览器使其有时间渲染UI,React则等待下一帧时间到来继续被中断的工作。

IO瓶颈

解决IO瓶颈旨在解决用户对网络延时的感知。

老的React架构

React 15架构

React 15分为两层:

  • Reconciler(协调器,找出变化的组件)
  • Renderer(渲染器,负责将变化的组件渲染到页面上)。
Reconciler v15

Reconciler的功能:

  1. 调用函数/class组件的render方法,将返回的JSX转化为虚拟DOM
  2. 将虚拟DOM与上次更新时的虚拟DOM做对比
  3. 本次更新中变化的虚拟DOM
  4. 通知Renderer将变化的虚拟DOM渲染到页面上
Renderer

Renderer有以下几种:

  1. ReactDOM:渲染到浏览器
  2. ReactNative:渲染到App原生组件
  3. ReactTest:输出纯JS对象用于测试
  4. ReactArt:渲染到Canvas,SVG或者VML(仅IE支持)

React 15架构的缺点

递归更新

更新一旦开始,中途就无法中断。Reconciler和Renderer是交替工作的。

解决方案:用可中断的异步更新代替同步的更新

新的React架构

React 16架构

比起React 15, React 16架构新增了一层:

  • Scheduler(调度器,调度任务的优先级,高优任务优先进入Reconciler)
Scheduler

部分浏览器有requestIdleCallback这个API用来在浏览器空闲时间调用函数, 但是React没有采用。而是使用了Scheduler这个独立于React的库(react/packages/scheduler/)。不使用的原因是:

  1. 浏览器兼容问题
  2. requestIdleCallback触发频率不稳定,在其它inactive的tab中这个API触发的频率会变得很低
  3. requestIdleCallback不够完备,Scheduler拥有多种调度优先级供任务设置。
Reconciler v16

更新工作从递归变成了可以中断的循环过程。每次循环都会调用shouldYield判断当前是否有剩余时间。

Reconciler与Renderer不再是交替工作。当Scheduler将任务交给Reconciler后,Reconciler会为变化的虚拟DOM打上代表增/删/更新的标记,之后再交给Renderer。

总结

由于React 16中,Scheduler和Reconciler执行之后,才会交给Renderer处理,而不是React 15中Reconciler和Renderer交替执行,如果中断(实际上React 15不会中断),用户不会看见更新不完全的DOM。

React Reconciler内部采用了Fiber的架构。

Fiber架构的心智模型

React核心团队成员Sebastian Markbåge(React Hooks的发明者)曾说:我们在React中做的就是践行代数效应(Algebraic Effects)。

代数效应是函数式编程中的一个概念,用于将副作用函数调用中分离。链接

代数效应在React当中的应用

  1. useState等Hooks中,我们不需要关注FunctionComponentstate在Hooks中是如何保存的,而是交给React处理
  2. React的Suspense中,我们可以使用同步的写法去完成异步请求(demo

代数效应与Generator

浏览器原生支持Generator,是一个类似的代数效应的实现。但是被React团队抛弃了——他们并没有使用Generator来实现Reconciler,原因是:

  1. Generator是传染性的
  2. Generator执行的中间状态是上下文关联的

代数效应与Fiber

几个概念:进程process、线程thread、协程coroutine、纤程fiber。

React Fiber:React内部实现的一套状态更新机制。支持任务不同优先级,可中断与恢复,并且恢复后可以复用之前的中间状态。

Fiber架构的实现原理

Fiber的起源

React 16将递归的无法中断的更新重构为异步的可中断更新,由于曾经用于递归的虚拟DOM数据结构已经无法满足需要。于是,全新的Fiber架构应运而生。

Fiber的含义

  1. 架构:React 15: stack reconciler, React 16: fiber reconciler - 基于Fiber节点实现
  2. 静态的数据结构:每一个Fiber节点对应一个React element,保存了该组件的类型(函数组件/类组件/原生组件)、对于应的DOM节点信息等等。
  3. 动态的工作单元:每一个Fiber节点保存了本次更新中该组件改变的状态、要执行的工作(增/删/改)

Fiber的结构

function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // 作为静态数据结构的属性
  this.tag = tag;                           // 类型 Function/Class/Host...
  this.key = key;                           
  this.elementType = null;                  // 大部分情况同type,除了memo包裹等情况
  this.type = null;                         // 对于 FunctionComponent,指函数本身
                                            // 对于ClassComponent,指class
                                            // 对于HostComponent,指DOM节点tagName
  this.stateNode = null;                    // Fiber对应的真实DOM节点

  // 用于连接其他Fiber节点形成Fiber树
  this.return = null;                       // 父节点 
  this.child = null;                        // 子节点
  this.sibling = null;                      // 右边第一个节点
  this.index = 0;

  this.ref = null;

  // 作为动态的工作单元的属性
  this.pendingProps = pendingProps;         // 保存本次更新造成的状态改变相关信息
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;
  this.dependencies = null;

  this.mode = mode;

  this.effectTag = NoEffect;                // 保存本次更新会造成的DOM操作
  this.nextEffect = null;

  this.firstEffect = null;
  this.lastEffect = null;

  // 调度优先级相关
  this.lanes = NoLanes;
  this.childLanes = NoLanes;

  // 指向该fiber在另一次更新时对应的fiber
  this.alternate = null;
}

Fiber架构的工作原理

双缓存

双缓存:在内存中构建并直接替换的技术

This is to set a time out function.

NAd we weill weeos hoihoh= how ei soeods on

赛阿萨啊大大树挪死搜号是得瑟得瑟