【React系列】初识 React 理念

697 阅读4分钟

前言

在下之前的技术栈是 Vue(虽然已经用了快一年的 React 了, Vue 也只是停留在 Vue2 的年代), 但为了更好地“生存”,以及团队发展的需要,将自顶向下学习 React, 并通过文章的形式巩固自己的学习成果。

设计理念

什么是 React?

官方说法是, React 是用 JavaScript 构建 快速响应 的大型 Web 应用程序的首选方式。它在 Facebook 和 Instagram 上表现优秀。

那 Vue 呢?

官方说法是, Vue 是一套用于构建用户界面的渐进式框架

React 的设计理念是什么?

从官方给出的定义来看,React 的设计理念是构建一个能 快速响应 的框架,而制约快速响应主要有两种因素: CPU (js 的计算) 与 IO (网络延迟)

而 React 采用 异步可中断的更新机制 来达到快速响应的目的。

如何实现异步可中断的更新机制

旧的 React 架构 (React15 之前)

协调器通过 diff 算法判断组件是否需要更新,通知到渲染器进行渲染更新,在这个过程中,协调器与渲染器依次交替执行,同步更新。 这套 React 架构无法采用异步可断的更新机制,即不再符合 React 建立快速响应的初衷,因此,在 React16对这套架构进行了重构。 image.png

新的 React 架构 (React16 以后)

为了践行 快速响应 的理念,React16 新增了 Scheduler(调度器),通过 调度器 来管理异步更新的优先级,高优先级的更新会更快的被调度。而调度器和协调器是在内存中实现的,并不会展示到用户层,因此用户是无法察觉到的。 image.png

如何实现

Fiber 架构

什么是 Fiber ?
一、从架构的角度上看

每个Fiber节点有个对应的React element,多个 Fiber节点 通过 child、sibling、return 连接形成树

image.png

二、从数据结构的角度上看

每个 Fiber 对应一个组件,作为一个数据结构,它保存了组件相关的信息

// Fiber对应组件的类型 Function/Class/Host...
this.tag = tag;
// key属性
this.key = key;
// 大部分情况同type,某些情况不同,比如FunctionComponent使用React.memo包裹
this.elementType = null;
// 对于 FunctionComponent,指函数本身,对于ClassComponent,指class,对于HostComponent,指DOM节点tagName
this.type = null;
// Fiber对应的真实DOM节点
this.stateNode = null;
三、作为动态的工作单元

例如 Fiber 上带有 Effect 的属性都是和副作用相关的

function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // 作为静态数据结构的属性
  this.tag = tag;
  this.key = key;
  this.elementType = null;
  this.type = null;
  this.stateNode = null;

  // 用于连接其他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;
  this.nextEffect = null;

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

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

  // 指向该fiber在另一次更新时对应的fiber
  this.alternate = null;
}
React 是如何通过 Fiber 更新 DOM 呢?

在提及更新 DOM 之前,我们先说一下什么叫双缓存技术,例如在更新页面时,如果我们先对页面进行卸载,在挂载新的页面,如果卸载、挂载时间过长,可能导致页面白屏。因此我们可以通过双缓存技术,先将需要挂载的页面进行虚拟渲染,将渲染后的结果存放在内存中,再进行页面的替换。

因此在 React 中,其渲染更新也是采用了双缓存的技术,即在卸载旧 Fiber 树(current Fiber 树)之前,会先在内存中构建新的 Fiber 树(workInProgress Fiber树, 这里也会通过 diff 算法进行 Fiber 节点的复用),当新 Fiber 树渲染完成后,通过fiberRootNodecurrent指针指向workInProgress Fiber树使其变为current Fiber 树

JSX

什么是 JSX

JSX 类似于 Vue 的 template 模版, Vue 会将 template 模版编译成 render 函数,而 React 会将 JSX 编译成 React.createElement 函数

JSX 与 Component 有啥关系呢?

如下图,我们知道 JSX 编译执行后的元素(React Element)的 type 属性,就是对应我们 Component

image.png

JSX 与 Fiber 关系

JSX是一种描述当前组件内容的数据结构,他不包含组件schedulereconcilerender所需的相关信息,而 Fiber 节点保存了调度优先级相关、组件的 state、以及是否被渲染过的信息。

所以,在组件mount时,Reconciler根据JSX描述的组件内容生成组件对应的Fiber节点

update时,ReconcilerJSXFiber节点保存的数据对比,生成组件对应的Fiber节点,并根据对比结果为Fiber节点打上标记

随便一提:刚开始一直很疑惑为啥是新的 JSX 和旧的 Fiber阶段保存的数据进行对比,而不是两颗 Fiber 树作对比,原因如下:

1 用 新的JSX 对比 旧Fiber 进行复用,可以减少生成新的 Fiber 节点步骤。

2 如果已经有新的 Fiber 树,那就没有必要对比了,直接用新的 Fiber 树就完事了