本文主要介绍一些React的设计思想和相关概念,不管是想要阅读源码还是想深入了解React的同学看过来呀。欢迎指出错误,一起探讨一起进步。
因为每次更新文章都要更新一遍目录太麻烦,所以本系列的文章都整合到Github,有兴趣阅读更多React源码阅读文章的同学可以用力点这里这里这里,如果觉得文章写得还行的话,对你有小小的帮助,希望可以给颗小星星给我,谢谢😜
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的主要特性就是各种组合而成的组件。由不同人编写的组件可以组合使用,并且实现组件的复用。
React的初始化,更新,移除等等操作并不是同步的,用户编写的组件(如<ReactComponent/>
)或者平台特定组件(如<div/>
)这些只是返回需要渲染的信息,并不是真实DOM,我们通常称他们为虚拟DOM,React会遍历这颗虚拟DOM树来渲染真实的DOM树。
而且setState()
也并不是同步的,他们的多次调用更新会被整合为一次更新,加入更新队列然后等待更新,再去渲染真实的DOM树,因为DOM型的操作通常是很耗时的,所以尽量减少DOM相关操作。
React的一个主要特点为编写的代码可以通过工具(如ReactNative,Electron)在多个平台上运行,因此渲染出来的元素可能不是DOM,因此React将他们分为两个模块,一个模块为reconciler(调节器),它根据用户编写的组件转换为React可以识别的元素(即虚拟DOM);另一个模块为renderer(渲染器,分为DOM渲染器和Native渲染器),它的作用为根据虚拟DOM渲染为平台特定的元素。这样的好处是可以方便React可以在多平台上运行,而不必太过关注reconciler的代码。
Fiber是React版本16以后的重要概念。
Fiber的主要目标为:
- 先暂停目前的工作去处理优先级更高的工作,然后再回来继续
- 为不同的工作设定不同优先级
- 重用之前完成的工作
- 终止不再需要的工作
我们都知道调用函数时会把它加入执行栈中,等函数执行完后再退出栈,那有没有办法可以定制栈的调用以更好地优化渲染UI呢?这就是Fiber想要解决的问题,我们可以称单个Fiber为虚拟的栈帧,它是实现React时序安排的关键。
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,
|};
这两个属性都是用来表示组件的,更新组件的时候先检查key
是否有改变,如果改变的话则直接摧毁重建;不变的话则继续检查type
是否改变,改变则直接摧毁重建;否则重用原来的组件,只是改变组件的属性。type
可能是用户定义的函数型或者类型的组合型组件,或者是代表平台元素的字符串(如'div'
)。
概念上来说,props
就是一个函数的参数数组。pendingProps
是Fiber开始执行的时候被设定的,memoizedProps
在执行完之后。每次更新的时候先比较pendingProps
和memoizedProps
,如果它们相同,则表示先前的输出可以重用,而不必进行多余的工作。
一个表面工作优先权的属性。除NoWork
(它的值为0)以外,数值越大代表有限权越低。
function matchesPriority(fiber, priority) {
return fiber.pendingWorkPriority !== 0 &&
fiber.pendingWorkPriority <= priority
}