Appearance
第18章 设计模式与架构决策
本章要点
- React 源码中 10 个核心设计模式的识别与深度剖析:从 Observer 到 Visitor 的工程实践
- 从 Class 到 Hooks 到 Compiler:三次 API 范式跃迁背后的设计哲学演变
- Algebraic Effects 与 Prepack:React 团队探索过又放弃的技术方案考古
- React vs Vue vs Svelte vs Solid:四大框架从响应式模型到编译策略的终极架构对比
- React 的技术决策方法论:为什么"选择不做什么"比"选择做什么"更重要
- React 下一个十年的技术趋势:从运行时框架到全栈编译器平台
写完前面十七章,我们已经从 JSX 编译、Fiber 架构、调度器、Reconciliation、Commit 阶段、Hooks 实现、并发模式、Server Components、React Compiler 等各个维度,完成了对 React 19 内核的全面解剖。如果把前面的章节比作"显微镜"——逐行逐函数地观察 React 机体的每一个细胞,那么本章我们需要换一种工具:望远镜。
站在足够远的距离回望 React 的源码,你会发现一个令人惊叹的事实:在那些看似复杂的实现细节之下,存在着一组反复出现的设计模式。它们不是教科书上的学术练习,而是 React 团队在十余年工程实践中,面对真实约束做出的真实选择。同时,React 的发展史本身就是一部"技术决策史"——每一次重大版本更新,都意味着一次架构哲学的重新审视。理解这些决策的"为什么",远比记住它们的"是什么"更有价值。
本章是全书的收官之章。我们将从设计模式、API 设计哲学、技术考古、框架对比、未来展望五个维度,为你构建一幅 React 架构决策的全景图。这不仅是对前面所有章节的一次高维度总结,更是帮助你建立框架设计者的思维方式——当你下次面对"为什么 React 要这样做"的问题时,你能从第一性原理给出答案。
18.1 React 源码中的 10 个核心设计模式
React 的源码从来不是为了展示设计模式而写的——它是为了解决问题。但当你用设计模式的"棱镜"去观察这些解决方案时,会发现 Gang of Four 书中的经典模式几乎无处不在。以下是 React 源码中最核心的 10 个设计模式,按照它们在渲染流程中出现的顺序排列。
18.1.1 Observer 模式:状态变更的订阅与通知
Observer(观察者)模式是 React 响应式系统的基石。当你调用 setState 时,React 需要知道哪些组件依赖了这个状态,然后通知它们重新渲染。这本质上就是一个发布-订阅关系。
typescript
// React 中 Observer 模式的核心体现:useState 的更新链路
// 文件:packages/react-reconciler/src/ReactFiberHooks.js
function dispatchSetState<S, A>(
fiber: Fiber,
queue: UpdateQueue<S, A>,
action: A
): void {
// 1. 创建 update 对象("事件")
const update: Update<S, A> = {
lane,
revertLane: NoLane,
action, // 用户传入的新值或 updater 函数
hasEagerState: false,
eagerState: null,
next: null as any,
};
// 2. 将 update 入队("通知"排队)
const alternate = fiber.alternate;
if (fiber === currentlyRenderingFiber ||
(alternate !== null && alternate === currentlyRenderingFiber)) {
// 渲染阶段的更新,特殊处理
didScheduleRenderPhaseUpdateDuringThisPass = true;
// ...
} else {
// 3. 调度更新("通知"观察者)
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
if (root !== null) {
scheduleUpdateOnFiber(root, fiber, lane); // 触发重新渲染
}
}
}这段代码是经典 Observer 模式的 React 变体。useState 返回的 setState 函数就是"发布"操作,而 scheduleUpdateOnFiber 则是通知调度器"有东西变了,需要重新渲染"。但与传统 Observer 不同的是,React 不直接通知每个组件,而是通知调度器——由调度器决定何时、以何种优先级去"通知"(重新渲染)组件。这是一种间接观察者模式,调度器充当了中介。
深度洞察:Vue 使用的是直接 Observer 模式——每个响应式属性都维护自己的依赖列表,变更时直接通知依赖的 effect。React 则选择了间接模式——任何状态变更都先进入调度器,由调度器统一协调。这两种选择没有绝对优劣,但它们深刻地影响了两个框架的性能特征:Vue 的更新粒度更细(组件级),React 的调度能力更强(可中断、可排优先级)。
18.1.2 Strategy 模式:可插拔的协调策略
Strategy(策略)模式允许算法在运行时被替换。React 的 Reconciler 就是一个巨大的策略容器——它不关心最终的渲染目标是 DOM、Native 组件还是字符串,只负责协调逻辑。
typescript
// React Reconciler 的策略注入接口
// 文件:packages/react-reconciler/src/ReactFiberReconciler.js
// HostConfig 就是策略接口——不同的宿主环境提供不同的实现
export type HostConfig = {
// 创建实例的策略
createInstance(
type: string,
props: Props,
rootContainerInstance: Container,
hostContext: HostContext,
internalInstanceHandle: Object
): Instance;
// 创建文本节点的策略
createTextInstance(
text: string,
rootContainerInstance: Container,
hostContext: HostContext,
internalInstanceHandle: Object
): TextInstance;
// 提交更新的策略
commitUpdate(
instance: Instance,
type: string,
oldProps: Props,
newProps: Props,
internalInstanceHandle: Object
): void;
// 添加子节点的策略
appendChild(parentInstance: Instance, child: Instance | TextInstance): void;
// ... 还有数十个策略方法
};React DOM 实现了一套 HostConfig,React Native 实现了另一套,React Three Fiber(3D 渲染)实现了又一套。Reconciler 的核心代码完全不需要改变——这就是 Strategy 模式的威力。
typescript
// react-dom 的策略实现
// 文件:packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
export function createInstance(
type: string,
props: Props,
rootContainerInstance: Container,
hostContext: HostContext,
internalInstanceHandle: Object
): Instance {
const domElement: Instance = createElement(type, props, rootContainerInstance, hostContext);
precacheFiberNode(internalInstanceHandle, domElement);
updateFiberProps(domElement, props);
return domElement;
}
// react-native 的策略实现则完全不同——创建的是 Native View 而不是 DOM Element18.1.3 Factory 模式:Fiber 节点的创建
Factory(工厂)模式在 React 中最显著的应用是 Fiber 节点的创建。React 需要根据不同的 Element 类型(函数组件、类组件、原生元素、Fragment、Portal 等)创建不同结构的 Fiber 节点。
typescript
// Fiber 创建的工厂逻辑
// 文件:packages/react-reconciler/src/ReactFiber.js
export function createFiberFromElement(
element: ReactElement,
mode: TypeOfMode,
lanes: Lanes
): Fiber {
const type = element.type;
const key = element.key;
const props = element.props;
let fiberTag: WorkTag = IndeterminateComponent; // 默认为"待确定"类型
if (typeof type === 'function') {
// 函数组件或类组件(此时还不确定,需要后续在 beginWork 中判断)
if (shouldConstruct(type)) {
fiberTag = ClassComponent;
} else {
fiberTag = FunctionComponent;
}
} else if (typeof type === 'string') {
// 原生 DOM 元素:div、span、p 等
fiberTag = HostComponent;
} else {
// 特殊类型:Fragment、Suspense、Portal 等
getTag: switch (type) {
case REACT_FRAGMENT_TYPE:
return createFiberFromFragment(props.children, mode, lanes, key);
case REACT_SUSPENSE_TYPE:
fiberTag = SuspenseComponent;
break getTag;
case REACT_SUSPENSE_LIST_TYPE:
fiberTag = SuspenseListComponent;
break getTag;
// ... 更多类型
}
}
const fiber = createFiber(fiberTag, props, key, mode);
fiber.elementType = type;
fiber.type = type;
fiber.lanes = lanes;
return fiber;
}注意这里有一个微妙的设计:函数组件最初被标记为 IndeterminateComponent,直到第一次 beginWork 时才确定它到底是函数组件还是类组件。这种"延迟决策"策略也是 Factory 模式的一种变体——工厂不急于确定产品的最终类型,而是留到必要时刻再决定。
18.1.4 Visitor 模式:Fiber 树的遍历
Visitor(访问者)模式允许你在不修改数据结构的情况下,为其添加新的操作。React 的 beginWork 和 completeWork 就是 Fiber 树上的两个"访问者"——它们遍历 Fiber 树,但对每种类型的 Fiber 节点执行不同的操作。
typescript
// beginWork 本质上是一个 Visitor 的 visit 方法
// 文件:packages/react-reconciler/src/ReactFiberBeginWork.js
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes
): Fiber | null {
// 根据 Fiber 节点的 tag(类型),分派到不同的处理函数
switch (workInProgress.tag) {
case FunctionComponent:
return updateFunctionComponent(current, workInProgress, /*...*/);
case ClassComponent:
return updateClassComponent(current, workInProgress, /*...*/);
case HostComponent:
return updateHostComponent(current, workInProgress, /*...*/);
case SuspenseComponent:
return updateSuspenseComponent(current, workInProgress, /*...*/);
case MemoComponent:
return updateMemoComponent(current, workInProgress, /*...*/);
case ForwardRef:
return updateForwardRef(current, workInProgress, /*...*/);
case LazyComponent:
return mountLazyComponent(current, workInProgress, /*...*/);
// ... 20+ 种节点类型
}
}这个巨大的 switch 语句就是 Visitor 模式的特征标志。每种 Fiber 类型定义了自己被"访问"时的行为,而遍历逻辑(workLoop → performUnitOfWork → beginWork/completeWork)与节点处理逻辑完全分离。
18.1.5 Command 模式:Update 与 Effect
Command(命令)模式将操作封装为对象,使得操作可以排队、撤销、重放。React 中有两个经典的 Command 模式应用:
Update 对象——每一次 setState 调用都被封装为一个 Update 命令:
typescript
// Update 就是一个 Command 对象
type Update<S, A> = {
lane: Lane; // 优先级
revertLane: Lane; // 回退优先级(用于 useOptimistic)
action: A; // 命令的载荷
hasEagerState: boolean; // 是否已提前计算
eagerState: S | null; // 提前计算的结果
next: Update<S, A> | null; // 链表下一个命令
};Effect 对象——useEffect、useLayoutEffect 产生的副作用也是 Command:
typescript
type Effect = {
tag: HookFlags; // 副作用类型标记
create: () => (() => void) | void; // 执行命令
destroy: (() => void) | void; // 撤销命令(cleanup)
deps: Array<mixed> | null; // 依赖条件
next: Effect | null; // 链表下一个命令
};这些 Command 对象被创建后不会立即执行,而是排入队列,等待 Reconciler 在合适的时机统一处理。这使得 React 能够实现批量更新(多个 setState 合并为一次渲染)和优先级调度(高优先级的 Update 先处理)。
18.1.6 Mediator 模式:Scheduler 作为中央调度者
Mediator(中介者)模式定义一个中介对象来封装一系列对象之间的交互。React 的 Scheduler 就是整个运行时的 Mediator——所有的更新请求都经过它,由它决定执行顺序和时机。
typescript
// Scheduler 作为 Mediator 的角色
// 文件:packages/scheduler/src/forks/Scheduler.js
function unstable_scheduleCallback(
priorityLevel: PriorityLevel,
callback: Callback,
options?: { delay: number }
): Task {
const currentTime = getCurrentTime();
const startTime = typeof options === 'object' && options !== null
? currentTime + options.delay
: currentTime;
// 根据优先级计算超时时间
let timeout: number;
switch (priorityLevel) {
case ImmediatePriority: timeout = -1; break;
case UserBlockingPriority: timeout = 250; break;
case NormalPriority: timeout = 5000; break;
case LowPriority: timeout = 10000; break;
case IdlePriority: timeout = maxSigned31BitInt; break;
}
const expirationTime = startTime + timeout;
const newTask: Task = {
id: taskIdCounter++,
callback,
priorityLevel,
startTime,
expirationTime,
sortIndex: -1,
};
// 根据开始时间分配到不同队列
if (startTime > currentTime) {
// 延迟任务 → timerQueue(小顶堆)
newTask.sortIndex = startTime;
push(timerQueue, newTask);
} else {
// 即时任务 → taskQueue(小顶堆)
newTask.sortIndex = expirationTime;
push(taskQueue, newTask);
}
// 请求调度
if (!isHostCallbackScheduled && !isPerformingWork) {
isHostCallbackScheduled = true;
requestHostCallback();
}
return newTask;
}Scheduler 不关心更新从哪来(用户交互?网络请求?定时器?),也不关心更新要做什么(重新渲染?提交 DOM?执行 Effect?)。它只负责根据优先级和时间约束,决定"什么时候执行什么"。所有模块都通过 Scheduler 这个 Mediator 进行间接通信。