上一章介绍了 React 的整体架构,其中讲到了 React 当中的 element 对象和 Fiber 虚拟 DOM 树,这一章就来具体讲一讲它们的结构以及之间的区别和联系。
element对象
element对象是 createElement 的产物,是类组件 render() 方法,函数组件执行返回的内容,经过React解析之后的对象,是对各类标签信息的汇总。
{
$$typeof: Symbol(react.element), // JSON不支持$$属性名,无法传输该字段名,防止xss攻击
key: null, // 组件的key,后面会传递给fiber,diff算法会用它优化
props: { // 组件属性,来源于父组件或者HOC,或者类似的外部传递
children: 'Hello World',// 子组件
className: 'hello' // 类名
},
ref: null, // 当前的dom引用
type: 'dev', // 组件类型
_owner: null // 指向向上最近的类组件或函数组件
}
了解了 element 对象的结构,我们再来看看 fiber 架构,先上图。
图中白色方块代表我们上面讲到的 element 对象,蓝色方块是它们对应的 fiber,React 中经常提到的 Fiber 架构就是图中蓝色方块构成的这棵树。
FiberRoot
fiberRoot 是 fiber 树的根节点的维护者,它的 current 属性指向的是当前缓存的虚拟 DOM 树的根节点,是我们常说的 current 树。
type FiberRootProperties = {
// root节点,render方法接收的第二个参数
containerInfo: any,
// 只有在持久更新中会用到,也就是不支持增量更新的平台,react-dom不会用到
pendingChildren: any,
// 当前应用对应的Fiber对象,是Root Fiber
current: Fiber,
// 一下的优先级是用来区分
// 1) 没有提交(committed)的任务
// 2) 没有提交的挂起任务
// 3) 没有提交的可能被挂起的任务
// 我们选择不追踪每个单独的阻塞登记,为了兼顾性能
// The earliest and latest priority levels that are suspended from committing.
// 最老和新的在提交的时候被挂起的任务
earliestSuspendedTime: ExpirationTime,
latestSuspendedTime: ExpirationTime,
// The earliest and latest priority levels that are not known to be suspended.
// 最老和最新的不确定是否会挂起的优先级(所有任务进来一开始都是这个状态)
earliestPendingTime: ExpirationTime,
latestPendingTime: ExpirationTime,
// The latest priority level that was pinged by a resolved promise and can
// be retried.
// 最新的通过一个promise被reslove并且可以重新尝试的优先级
latestPingedTime: ExpirationTime,
// 如果有错误被抛出并且没有更多的更新存在,我们尝试在处理错误前同步重新从头渲染
// 在`renderRoot`出现无法处理的错误时会被设置为`true`
didError: boolean,
// 正在等待提交的任务的`expirationTime`
pendingCommitExpirationTime: ExpirationTime,
// 已经完成的任务的FiberRoot对象,如果你只有一个Root,那他永远只可能是这个Root对应的Fiber,或者是null
// 在commit阶段只会处理这个值对应的任务
finishedWork: Fiber | null,
// 在任务被挂起的时候通过setTimeout设置的返回内容,用来下一次如果有新的任务挂起时清理还没触发的timeout
timeoutHandle: TimeoutHandle | NoTimeout,
// 顶层context对象,只有主动调用`renderSubtreeIntoContainer`时才会有用
context: Object | null,
pendingContext: Object | null,
// 用来确定第一次渲染的时候是否需要融合
+hydrate: boolean,
// 当前root上剩余的过期时间
nextExpirationTimeToWorkOn: ExpirationTime,
// 当前更新对应的过期时间
expirationTime: ExpirationTime,
// List of top-level batches. This list indicates whether a commit should be
// deferred. Also contains completion callbacks.
// 顶层批次(批处理任务?)这个变量指明一个commit是否应该被推迟
// 同时包括完成之后的回调
// 貌似用在测试的时候?
firstBatch: Batch | null,
// root之间关联的链表结构
nextScheduledRoot: FiberRoot | null,
};
Fiber
Fiber 对应一个组件或者原生标签在 React 事件流中的数据结构。
type Fiber = {
// 标记不同的组件类型,例如函数、类组件
tag: WorkTag,
// ReactElement里面的key
key: null | string,
// ReactElement.type,jsx具体标签名,也就是我们调用
// `createElement`的第一个参数
elementType: any,
// The resolved function/class/ associated with this fiber.
// 异步组件resolved之后返回的内容,一般是`function`或者`class`
// 组件
type: any,
// The local state associated with this fiber.
// 跟当前Fiber相关本地状态(比如浏览器环境就是DOM节点)
stateNode: any,
// 指向在Fiber节点树中的`parent`,用来在处理完这个节点之后向上返回
return: Fiber | null,
// 单链表树结构
// 指向自己的第一个子节点
child: Fiber | null,
// 指向自己的兄弟结构
// 兄弟节点的return指向同一个父节点
sibling: Fiber | null,
index: number,
// ref属性
ref: null | (((handle: mixed) => void) & {_stringRef: ?string}) | RefObject,
// 新的变动带来的新的props
pendingProps: any,
// 上一次渲染完成之后的props
memoizedProps: any,
// 该Fiber对应的组件产生的Update会存放在这个队列里面
updateQueue: UpdateQueue<any> | null,
// 上一次渲染的时候的state,函数组件中为Hooks链表
memoizedState: any,
// 一个列表,存放这个Fiber依赖的context
firstContextDependency: ContextDependency<mixed> | null,
// 用来描述当前Fiber和它子树的`Bitfield`
// 共存的模式表示这个子树是否默认是异步渲染的
// Fiber被创建的时候他会继承父Fiber
// 其他的标识也可以在创建的时候被设置
// 但是在创建之后不应该再被修改,特别是他的子Fiber创建之前
mode: TypeOfMode,
// Effect
// 用来记录Side Effect
effectTag: SideEffectTag,
// 单链表用来快速查找下一个side effect
nextEffect: Fiber | null,
// 子树中第一个side effect
firstEffect: Fiber | null,
// 子树中最后一个side effect
lastEffect: Fiber | null,
// 代表任务在未来的哪个时间点应该被完成
// 不包括他的子树产生的任务
expirationTime: ExpirationTime,
// 快速确定子树中是否有不在等待的变化
childExpirationTime: ExpirationTime,
// 在Fiber树更新的过程中,每个Fiber都会有一个跟其对应的Fiber
// 我们称他为`current <==> workInProgress`
// 在渲染完成之后他们会交换位置
alternate: Fiber | null,
// 下面是调试相关的,收集每个Fiber和子树渲染时间的
actualDuration?: number,
// If the Fiber is currently active in the "render" phase,
// This marks the time at which the work began.
// This field is only set when the enableProfilerTimer flag is enabled.
actualStartTime?: number,
// Duration of the most recent render time for this Fiber.
// This value is not updated when we bailout for memoization purposes.
// This field is only set when the enableProfilerTimer flag is enabled.
selfBaseDuration?: number,
// Sum of base times for all descedents of this Fiber.
// This value bubbles up during the "complete" phase.
// This field is only set when the enableProfilerTimer flag is enabled.
treeBaseDuration?: number,
// Conceptual aliases
// workInProgress : Fiber -> alternate The alternate used for reuse happens
// to be the same as work in progress.
// __DEV__ only
_debugID?: number,
_debugSource?: Source | null,
_debugOwner?: Fiber | null,
_debugIsCurrentlyTiming?: boolean,
};
后面的章节中会讲到 React 是如何根据 element 对象生成 Fiber 树的,这也是 diff 算法的核心内容。