前端
漫游 React 源码 - 01 - 架构和目录结构
React 架构
React 架构主要分为三部分:
- Reconciler(协调器):负责比较虚拟 DOM 和真实 DOM,找出差异,然后更新真实 DOM。
- Renderer(渲染器):负责将虚拟 DOM 渲染到真实 DOM。
- Scheduler(调度器):负责调度任务,确保任务按优先级执行。
这三部分协同工作,Reconciler找出需要更新的内容,Scheduler安排更新的优先级和时机,Renderer最终将更新渲染到UI上。
React 源码目录结构
packages/
:React 代码库的主要目录,包含了 React 的所有包和模块。主要的子目录包括:
react
: 核心 React 包,包含 React 的主要功能实现。react-dom
: 处理与 DOM 交互的包。react-reconciler
: 负责协调虚拟 DOM 和真实 DOM。scheduler
: 任务调度器,用于控制更新的优先级。
scripts/
:- 包含了用于构建、测试和发布 React 的脚本。
fixtures/
:- 包含了 React 的示例和测试用例,通常用于验证和展示 React 的功能。
基本结构
列出一些重要的数据结构和方法,之后可以随时回来查阅。
Fiber
从 React 16 开始,React 引入了 Fiber 架构,用于解决之前版本中存在的性能问题。Reconciler 就是是基于 Fiber 架构实现的。 Fiber 是 React 对组件更新过程进行分片和优先级管理的基础单元,每个 Fiber 代表组件树中的一个节点,不仅包含了组件的相关属性,还有更新队列、更新优先级等信息。
export type Fiber = {
// 用于标识 Fiber 类型的标签。
tag: WorkTag, // 标识 Fiber 类型的标签(例如 FunctionComponent、ClassComponent 等)。
key: null | string, // 用于在同一层级的子节点中唯一标识 Fiber 节点。
elementType: any, // Fiber 节点对应的 React 元素类型。
type: any, // Fiber 节点的组件类型或元素类型。div,span
stateNode: any, // Fiber 节点的本地状态,对于类组件是组件实例,对于 DOM 节点是实际的 DOM 节点。
return: Fiber | null, // 指向父节点的指针。
child: Fiber | null, // 指向第一个子节点的指针。
sibling: Fiber | null, // 指向下一个兄弟节点的指针。
index: number, // 子节点在父节点中的位置索引。
ref:
|
null |
(((handle: mixed) => void) & {
_stringRef: ? string,
...
}) // React ref 对象。
|
RefObject,
refCleanup: null | (() => void), // 用于清理 ref 的回调。
pendingProps: any, // 传入的 props,表示即将生效的属性。
memoizedProps: any, // 之前的 props
updateQueue: mixed, // 状态更新队列,存储组件状态的更新。
memoizedState: any, // 记录的状态,表示上一次渲染的组件状态。
dependencies: Dependencies | null, // 组件的上下文依赖,通常用于 Context API。
mode: TypeOfMode, // Fiber 模式标志,用于启用如异步渲染等模式。
// Effect
flags: Flags, // 用于标记副作用的标志位。
subtreeFlags: Flags, // 子树中需要处理的副作用标志。
deletions: Array < Fiber > | null, // 需要删除的子节点数组。
// 该 Fiber 节点所属的更新优先级。
lanes: Lanes, // 更新优先级,标记该 Fiber 对应的更新在哪条“车道”中。是number类型
childLanes: Lanes, // 子节点的更新优先级。
alternate: Fiber | null, // 备用 Fiber,指向该 Fiber 的上一次渲染版本。
actualDuration ? : number, // 当前更新过程中此 Fiber 的实际渲染时间。
actualStartTime ? : number, // 此 Fiber 开始渲染的时间。
selfBaseDuration ? : number, // 记录此 Fiber 最近一次渲染的基础时间(不考虑 bailout)。
treeBaseDuration ? : number, // 记录整个子树的基础渲染时间。
// 概念上的别名
// workInProgress : Fiber -> alternate。备用的 Fiber 与工作中的 Fiber 相同。
// 即“双缓存”
};
FiberRoot
FiberRoot 是 React 应用的根节点,包含了应用的整个状态和上下文。
// react/src/react-reconciler/src/ReactFiberRoot.js
export type FiberRoot = {
...BaseFiberRootProperties,
...SuspenseCallbackOnlyFiberRootProperties,
...UpdaterTrackingOnlyFiberRootProperties,
...TransitionTracingOnlyFiberRootProperties,
...
};
type BaseFiberRootProperties = {
tag: RootTag, // 根节点的类型(legacy, batched, concurrent 等)
containerInfo: Container, // 与该根节点相关联的宿主环境的附加信息
pendingChildren: any, // 仅用于持久更新
// 顶层上下文对象,用于 renderSubtreeIntoContainer
context: Object | null,
// 用于创建一个链表,表示所有有待处理工作调度的根节点。
next: FiberRoot | null,
// 表示当前显示的 UI 树的根节点
current: Fiber,
// 表示已完成的 work-in-progress HostRoot,已准备好提交。
finishedWork: Fiber | null,
// Scheduler.scheduleCallback 返回的节点。表示根节点将处理的下一个渲染任务。
callbackNode: any,
callbackPriority: Lane,
expirationTimes: LaneMap < number > ,
hiddenUpdates: LaneMap < Array < ConcurrentUpdate > | null > ,
pendingLanes: Lanes,
suspendedLanes: Lanes,
pingedLanes: Lanes,
expiredLanes: Lanes,
errorRecoveryDisabledLanes: Lanes,
shellSuspendCounter: number,
finishedLanes: Lanes,
entangledLanes: Lanes,
entanglements: LaneMap < Lanes > ,
pooledCache: Cache | null,
pooledCacheLanes: Lanes,
};
React. Component
Component 是所有类组件的基类。它提供了组件的生命周期方法和状态管理机制。
// react/src/react/src/ReactBaseClasses.js
class Component {
constructor(props, context, updater) {
this.props = props;
this.context = context;
this.refs = refs;
this.updater = updater || ReactNoopUpdateQueue; // 更新队列
}
}
Component.prototype.setState = function(partialState, callback) {
...
this.updater.enqueueSetState(this, partialState, callback, 'setState');
// 将状态更新添加到更新队列中
};
Component.prototype.forceUpdate = function(callback) {
this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
// 将强制更新添加到更新队列中
};
ReactElement
ReactElement 是 React 中用于表示 UI 元素的对象。
// react/src/jsx/ReactJSXElement.js
function ReactElement(
type, //元素的类型(标签名或组件
key, //唯一标识元素的键,用于优化列表渲染。
_ref, //用于引用 DOM 节点或组件实例的引用。
self,
source,
owner,
props, //元素的属性,包括 children 和其他用户定义的属性。
debugStack,
debugTask,
) {
...
}
const element = React.createElement(
'div', {
className: 'container'
},
'Hello, World!'
);
/**
* 创建一个 React 元素。
* @param {string | React.ComponentType} type - 元素的类型,可以是标签名或 React 组件。
* @param {object} config - 元素的配置对象,包含属性、事件等。
* @param {...any} children - 元素的子元素。
* @returns {ReactElement} 返回一个 ReactElement 对象。
*/
export function createElement(type, config, children) {
const props = {}; // 初始化 props 对象, 之后会将 config 和 children 填充到 props 中。
...
}
在React中,会大量使用
if (__DEV__) { ... }
,在开发模式下,会执行一些额外的检查和日志记录,以确保代码的正确性和性能。有助于捕获潜在的 bug,而不影响生产环境。