React源码阅读:概况

1,779 阅读4分钟

前言

本文主要介绍一些React的设计思想和相关概念,不管是想要阅读源码还是想深入了解React的同学看过来呀。欢迎指出错误,一起探讨一起进步。

React阅读系列文章

React源码阅读:虚拟DOM的初始化

项目结构

React的相关代码都放在packages里。

├── packages ------------------------------- React实现的相关代码
│   ├── create-subscription ------------------------- 在组件里订阅额外数据的工具
│   ├── events -------------------------- React事件相关
│   ├── react-art ------------------------- 画图相关库
│   ├── react-dom -------------------------- ReactDom
│   ├── react-native-renderer ----------------------------- ReactNative
│   ├── react-reconciler ------------------------ React调制器
│   ├── react-scheduler ------------------------ 规划React初始化,更新等等
│   ├── react-test-renderer ------------------------ 实验性的React渲染器
│   ├── shared ------------------------ 公共代码
│   ├── simple-cache-provider ------------------------ 为React应用提供缓存

主要思想和设计原则

组合

React的主要特性就是各种组合而成的组件。由不同人编写的组件可以组合使用,并且实现组件的复用。

时序安排(Scheduling)

React的初始化,更新,移除等等操作并不是同步的,用户编写的组件(如<ReactComponent/>)或者平台特定组件(如<div/>)这些只是返回需要渲染的信息,并不是真实DOM,我们通常称他们为虚拟DOM,React会遍历这颗虚拟DOM树来渲染真实的DOM树。

而且setState()也并不是同步的,他们的多次调用更新会被整合为一次更新,加入更新队列然后等待更新,再去渲染真实的DOM树,因为DOM型的操作通常是很耗时的,所以尽量减少DOM相关操作。

DOM之外

React的一个主要特点为编写的代码可以通过工具(如ReactNative,Electron)在多个平台上运行,因此渲染出来的元素可能不是DOM,因此React将他们分为两个模块,一个模块为reconciler(调节器),它根据用户编写的组件转换为React可以识别的元素(即虚拟DOM);另一个模块为renderer(渲染器,分为DOM渲染器和Native渲染器),它的作用为根据虚拟DOM渲染为平台特定的元素。这样的好处是可以方便React可以在多平台上运行,而不必太过关注reconciler的代码。

相关概念

Fiber

什么是Fiber?

Fiber是React版本16以后的重要概念。

Fiber的主要目标为:

  • 先暂停目前的工作去处理优先级更高的工作,然后再回来继续
  • 为不同的工作设定不同优先级
  • 重用之前完成的工作
  • 终止不再需要的工作

我们都知道调用函数时会把它加入执行栈中,等函数执行完后再退出栈,那有没有办法可以定制栈的调用以更好地优化渲染UI呢?这就是Fiber想要解决的问题,我们可以称单个Fiber为虚拟的栈帧,它是实现React时序安排的关键。

Fiber的结构

type Fiber = {|
  //  辨识组件相关的属性
  tag: TypeOfWork,
  key: null | string,
  type: any,
  
  //  父Fiber或者null
  return: Fiber | null,

  child: Fiber | null,
  sibling: Fiber | null,
  index: number,
  ref: null | (((handle: mixed) => void) & {_stringRef: ?string}) | RefObject,

  memoizedProps: any, // The props used to create the output.
  updateQueue: UpdateQueue<any> | null,
  memoizedState: any,
  
  mode: TypeOfMode,

  effectTag: TypeOfSideEffect,
  nextEffect: Fiber | null,
  firstEffect: Fiber | null,
  lastEffect: Fiber | null,
  
  expirationTime: ExpirationTime,

  alternate: Fiber | null,

  //  时间
  actualDuration?: number,
  actualStartTime?: number,
  selfBaseTime?: number,
  treeBaseTime?: number,

 //  调试
  _debugID?: number,
  _debugSource?: Source | null,
  _debugOwner?: Fiber | null,
  _debugIsCurrentlyTiming?: boolean,
|};

keytype

这两个属性都是用来表示组件的,更新组件的时候先检查key是否有改变,如果改变的话则直接摧毁重建;不变的话则继续检查type是否改变,改变则直接摧毁重建;否则重用原来的组件,只是改变组件的属性。type可能是用户定义的函数型或者类型的组合型组件,或者是代表平台元素的字符串(如'div')。

pendingPropsmemoizedProps

概念上来说,props就是一个函数的参数数组。pendingProps是Fiber开始执行的时候被设定的,memoizedProps在执行完之后。每次更新的时候先比较pendingPropsmemoizedProps,如果它们相同,则表示先前的输出可以重用,而不必进行多余的工作。

pendingWorkPriority

一个表面工作优先权的属性。除NoWork(它的值为0)以外,数值越大代表有限权越低。

function matchesPriority(fiber, priority) {
  return fiber.pendingWorkPriority !== 0 &&
         fiber.pendingWorkPriority <= priority
}

参考文献