通过我们的32节课的源码学习,实现一个最小可运行的 React 子集,包括以下核心功能:
功能 | 描述 |
---|---|
JSX 解析 | 将 JSX 转换为虚拟 DOM(VDOM) |
Virtual DOM 构建 | 表示组件结构和属性 |
Fiber 构建与渲染 | 使用 Fiber 架构进行递归构建和渲染 |
更新队列与调度机制 | 支持异步调度任务 |
Hook 支持 | 实现基本的 useState 和 useEffect |
✅ 注意: 这是一个教学性质的简化版本,不用于生产环境。
my-react/
├── index.html # 应用入口HTML文件(挂载React应用的根容器)
├── index.tsx # TypeScript入口文件(启动React应用的核心逻辑)
├── react/ # React核心实现目录(自定义迷你React框架源码)
│ ├── jsx.ts # JSX编译器(将JSX语法转换为虚拟DOM)
│ ├── vdom.ts # 虚拟DOM管理(创建/更新/对比虚拟DOM树)
│ ├── fiber.ts # ⚡ Fiber架构核心(实现协调算法与调度单元)
│ ├── reconciler.ts # 协调器(Diff算法实现与DOM操作指令生成)
│ ├── hook.ts # 自定义Hooks系统(函数组件状态管理)
│ └── scheduler.ts # 调度器(实现优先级调度与时间分片)
└── components/ # 组件目录(存放业务组件)
└── App.tsx # 根组件(应用入口组件,包含核心业务逻辑)
┌──────────────────────┐
│ index.html │ 容器层
└──────────────────────┘
│
▼
┌──────────────────────┐
│ index.tsx │ 入口层
│ (挂载点 + 初始渲染) │
└──────────────────────┘
│
▼
┌──────────────────────────────┐
│ react/ │ 核心引擎层
├──────────────────────────────┤
│ ┌──────────┐ ┌──────────┐ │
│ │ jsx.ts │ │ vdom.ts │ │ 虚拟DOM层
│ └──────────┘ └──────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ fiber.ts │ │ ⚡ Fiber架构
│ │(双缓冲/协调) │ │
│ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ reconciler.ts│ │ Diff算法
│ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ hook.ts │ │ Hooks系统
│ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ scheduler.ts │ │ 调度系统
│ └─────────────┘ │
└──────────────────────────────┘
│
▼
┌──────────────────────┐
│ components/App.tsx │ 业务组件层
└──────────────────────┘
入口层 (index.tsx
)
ReactDOM.render
等价实现)wipRoot
)nextUnitOfWork
调度)虚拟DOM层
jsx.ts
: 实现JSX到createElement
的转换vdom.ts
:
TEXT_ELEMENT
特殊处理)children
)Fiber架构
child/sibling/return
指针实现树遍历alternate
指针实现旧/新树对比effectTag
记录DOM操作指令协调器 (reconciler.ts
)
effectList
)Hooks系统
HookState
通过next
指针串联alternate
实现跨Fiber版本状态同步调度器
requestIdleCallback
实现nextUnitOfWork
指针驱动调度流程分层解耦:
type
字段实现函数组件/类组件的统一处理增量渲染:
状态管理:
alternate
实现状态热更新副作用处理:
effectTag
标记DOM操作此架构实现了React核心特性(Fiber架构、Hooks、协调算法)的简化版实现,适合大家用于理解现代React的内部工作原理。
jsx.ts
算法设计思路:
(老曹建议手动coding实现思路)
[开始] → [调用createElement]
├─ [创建DOM对象]
├─ [处理props]
├─ [遍历children]
│ ├─ [判断child类型]
│ │ ├─ [对象 → 保留] → [收集]
│ │ └─ [原始值 → 调用createTextElement]
│ │ └─ [创建TEXT_ELEMENT节点] → [收集]
│ └─ [合并children]
└─ [返回虚拟DOM] → [结束]
算法详细实现:
// react/jsx.ts
// 导出创建虚拟DOM元素的函数
export function createElement(
type: string | Function, // 元素类型(可以是HTML标签名或自定义组件函数)
props: any, // 元素属性对象(包含className、style等)
...children: any[] // 剩余参数形式的子元素(可以是字符串、数字或其他元素)
) {
return {
type, // 元素类型(与参数type对应)
props: {
// 合并原始props和自动处理的children
...props,
// 标准化处理子元素:将非对象类型的子元素转换为文本元素
children: children.map(child =>
// 如果子元素是对象(如其他虚拟DOM元素),则直接保留
// 如果是原始值(字符串/数字),则创建对应的文本节点
typeof child === 'object' ? child : createTextElement(child)
),
},
};
}
// 创建文本虚拟DOM元素的专用函数
function createTextElement(text: string) {
return {
type: 'TEXT_ELEMENT', // 标识这是一个文本元素类型
props: {
nodeValue: text, // 存储文本内容(类似DOM的nodeValue属性)
children: [], // 文本元素没有子元素(始终为空数组)
},
};
}
在 index.tsx
中使用:
/** @jsxRuntime classic */
/** @jsx createElement */
import { createElement } from './react/jsx';
关键点说明:
type
参数可以是字符串(HTML标签)或函数(组件)...props
保留原始属性,同时添加标准化处理的childrenTEXT_ELEMENT
类型区分普通元素和文本节点,文本内容存储在nodeValue属性中...children
收集所有子元素参数,支持可变数量的子元素这种设计模式常用于虚拟DOM实现(比如React-like库),通过标准化元素结构,为后续的DOM diff和 reconciliation 过程提供便利。
vdom.ts
// 定义虚拟DOM节点的类型别名
export type VNode = {
// 元素类型:可以是HTML标签名(如'div')或组件函数
type: string | Function;
// 元素属性对象:使用Record类型表示键值对结构
// 键为属性名(字符串类型),值为任意类型(支持所有属性类型)
props: Record<string, any>;
// 子元素数组:递归类型定义,子元素必须是VNode类型
// 形成树形结构,每个节点都可以包含子节点
children: VNode[];
};
类型定义解析:
VNode
是虚拟DOM(Virtual Node)的核心类型定义type
字段:
props
字段:
Record
工具类型{ [key: string]: any }
,表示键值对形式的属性集合children
字段:
这个类型定义与之前实现的 createElement
函数完美匹配,共同构成了虚拟DOM的基础结构:
createElement
函数返回的值符合VNode类型type
对应组件/标签类型props
包含所有属性及处理后的子元素children
经过标准化处理为VNode数组这种设计模式是现代前端框架(如React、Preact)实现虚拟DOM的核心机制,通过类型系统保证节点结构的可预测性,同时为后续的DOM diff算法和协调过程(Reconciliation)提供类型安全的基础。
fiber.ts
React Fiber 架构核心数据结构及渲染入口的算法流程图解析(使用 Mermaid 语法):
渲染入口流程(render)
DOM创建流程(createDom)
属性设置流程
Fiber节点结构:
type Fiber = {
type?: any; // 元素类型标识
key?: any; // 列表重组标识
props?: Record<string, any>; // 属性对象
dom: HTMLElement | Text | null; // 关联DOM节点
parent: Fiber | null; // 父节点指针
child: Fiber | null; // 子节点链表
sibling: Fiber | null; // 兄弟节点链表
alternate?: Fiber | null; // 双缓冲指针
effectTag?: EffectTypes; // 副作用标记
hooks?: HookState[]; // 函数组件状态
}
双缓冲机制:
alternate
指针实现时间分片更新副作用系统:
effectTag
标记需要执行的DOM操作UPDATE
/PLACEMENT
/DELETION
三种操作类型链表结构:
child
指针建立sibling
指针建立开始
│
├─ 调用 render(element, container)
│ │
│ ├─ 创建初始根Fiber节点 (wipRoot)
│ │ ├─ dom: 关联容器
│ │ ├─ props.children: [element]
│ │ └─ parent: null
│ │
│ └─ 设置 nextUnitOfWork = wipRoot
│
├─ 进入协调循环 (while(nextUnitOfWork !== null))
│ │
│ ├─ 处理当前工作单元 (nextUnitOfWork)
│ │ │
│ │ ├─ 调用 createDom(fiber)
│ │ │ │
│ │ │ ├─ 判断是否为文本元素 (type === 'TEXT_ELEMENT')
│ │ │ │ ├─ 是 → 创建文本节点 document.createTextNode('')
│ │ │ │ └─ 否 → 创建元素节点 document.createElement(type)
│ │ │ │
│ │ │ └─ 设置DOM属性
│ │ │ ├─ 过滤props.children
│ │ │ └─ 遍历剩余属性设置到DOM
│ │ │
│ │ └─ 处理子节点 (fiber.child)
│ │ ├─ 存在子节点 → nextUnitOfWork = child
│ │ └─ 无子节点 → 处理兄弟节点 (fiber.sibling)
│ │ ├─ 存在兄弟节点 → nextUnitOfWork = sibling
│ │ └─ 回溯父节点 (fiber.parent)
│ │ └─ 继续向上查找兄弟节点
│ │
│ └─ 协调循环继续...
│
├─ 协调完成 (nextUnitOfWork === null)
│ │
│ ├─ 提交阶段 (commitPhase)
│ │ ├─ 将wipRoot的DOM树插入容器 (container.appendChild(wipRoot.dom))
│ │ └─ 更新currentRoot = wipRoot
│ │
│ └─ 重置wipRoot = null
│
结束
关键流程节点说明:
初始化阶段:
协调循环:
DOM创建:
提交阶段:
简化记忆版:
(老曹建议手动coding实现思路)
[开始] → [render()]
├─ [创建wipRoot]
├─ [设置nextUnitOfWork]
├─ [协调循环]
│ ├─ [处理当前Fiber]
│ │ ├─ [createDom()]
│ │ │ ├─ [文本节点?]
│ │ │ ├─ [创建元素节点]
│ │ │ └─ [设置属性]
│ │ ├─ [处理child]
│ │ ├─ [处理sibling]
│ │ └─ [回溯parent]
│ └─ [循环条件判断]
├─ [提交阶段]
│ ├─ [DOM插入容器]
│ └─ [更新currentRoot]
└─ [结束]
渲染算法实现:
// 定义Fiber节点类型(React Fiber架构的核心数据结构)
export type Fiber = {
type?: any; // 元素类型(标签名/组件函数/文本类型等)
key?: any; // 用于列表重组的唯一标识
props?: Record<string, any>; // 元素属性对象
dom: HTMLElement | Text | null; // 对应的真实DOM节点(文本节点或元素节点)
parent: Fiber | null; // 父级Fiber节点(形成树形结构)
child: Fiber | null; // 第一个子节点
sibling: Fiber | null; // 下一个兄弟节点
alternate?: Fiber | null; // 对应更新前的Fiber节点(用于双缓冲技术)
effectTag?: 'UPDATE' | 'PLACEMENT' | 'DELETION'; // 副作用标识(增/删/改)
hooks?: HookState[]; // 函数组件的钩子状态数组
};
// 当前已提交的根Fiber节点(对应已渲染的DOM树)
export let currentRoot: Fiber | null = null;
// 正在构建中的工作进阶根Fiber节点(对应未提交的更新)
export let wipRoot: Fiber | null = null;
// 协调器的工作单元指针(指向下一个需要处理的Fiber节点)
let nextUnitOfWork: Fiber | null = null;
/**
* 根据Fiber节点创建真实DOM元素
* @param fiber - 要创建DOM的Fiber节点
* @returns 创建的DOM元素(文本节点或普通元素)
*/
export function createDom(fiber: Fiber): HTMLElement | Text {
// 判断是否为文本元素(通过type标识)
const isTextElement = fiber.type === 'TEXT_ELEMENT';
// 创建对应DOM节点(文本节点或普通元素)
const dom = isTextElement
? document.createTextNode('') // 创建空文本节点
: document.createElement(fiber.type as string); // 类型断言为string
// 过滤并设置属性(排除children属性)
const isProperty = (key: string) => key !== 'children';
Object.keys(fiber.props || {}) // 防止props为undefined
.filter(isProperty)
.forEach(key => {
// 使用类型断言绕过TypeScript的类型检查(实际开发中建议更严谨的类型处理)
(dom as any)[key] = fiber.props[key];
});
return dom;
}
/**
* 渲染入口函数(启动协调过程)
* @param element - 要渲染的虚拟DOM元素
* @param container - 容器DOM元素
*/
export function render(element: any, container: HTMLElement) {
// 构建初始根Fiber节点(工作进阶根节点)
wipRoot = {
dom: container, // 关联容器DOM
props: {
children: [element], // 初始子元素(要渲染的虚拟DOM)
},
parent: null, // 根节点无父节点
child: null, // 初始无子节点
sibling: null, // 初始无兄弟节点
};
nextUnitOfWork = wipRoot; // 启动协调过程
}
reconciler.ts
以下是 React 协调器核心算法的流程图解析:
performUnitOfWork 主流程
reconcileChildren 协调流程
alternate
指针复用旧Fiber树,实现增量更新算法代码实现:
// react/reconciler.ts
// React 协调器核心实现(基于Fiber架构)
import { Fiber, createDom } from './fiber';
/**
* 执行单个工作单元(Fiber节点)
* @param fiber - 当前要处理的Fiber节点
* @returns 下一个需要处理的Fiber节点(形成链表遍历)
*/
export function performUnitOfWork(fiber: Fiber): Fiber | null {
// 1. 创建DOM节点(仅首次渲染时执行)
if (!fiber.dom && fiber.type) {
fiber.dom = createDom(fiber);
}
// 2. 协调子节点(对比新旧虚拟DOM)
const elements = fiber.props?.children || [];
reconcileChildren(fiber, elements);
// 3. 返回下一个工作单元(深度优先遍历)
if (fiber.child) {
return fiber.child; // 优先处理子节点
}
// 子节点处理完成后,回溯处理兄弟节点
let next: Fiber | null = fiber;
while (next) {
if (next.sibling) {
return next.sibling; // 存在兄弟节点则返回
}
next = next.parent; // 回溯到父节点继续查找
}
return null; // 所有节点处理完成
}
/**
* 协调子节点(Diff算法核心)
* @param wipFiber - 当前正在构建的Fiber节点(WorkInProgress)
* @param elements - 新的虚拟DOM元素数组
*/
function reconcileChildren(wipFiber: Fiber, elements: Fiber[]) {
let index = 0;
let oldFiber = wipFiber.alternate?.child; // 获取旧Fiber链表的第一个子节点
let prevSibling: Fiber | null = null;
while (index < elements.length || oldFiber != null) {
const element = elements[index];
let newFiber: Fiber | null = null;
// 类型相同:复用旧Fiber节点(双缓冲技术)
const sameType = oldFiber && element && element.type === oldFiber.type;
if (sameType) {
// 更新节点(保留旧DOM引用)
newFiber = {
type: oldFiber!.type,
props: element.props,
parent: wipFiber,
alternate: oldFiber,
effectTag: 'UPDATE',
dom: oldFiber!.dom, // 复用旧DOM节点
child: null,
sibling: null,
hooks: [],
};
}
// 新增节点(element存在但oldFiber不存在)
else if (element) {
newFiber = {
type: element.type,
props: element.props,
parent: wipFiber,
alternate: null,
effectTag: 'PLACEMENT', // 标记为插入操作
dom: null,
child: null,
sibling: null,
hooks: [],
};
}
// 删除节点(oldFiber存在但element不存在)
else if (oldFiber) {
oldFiber.effectTag = 'DELETION'; // 标记为删除操作
}
// 移动旧Fiber指针
if (oldFiber) {
oldFiber = oldFiber.sibling;
}
// 构建新Fiber链表
if (index === 0) {
wipFiber.child = newFiber; // 第一个子节点
} else if (element && prevSibling) {
prevSibling.sibling = newFiber; // 兄弟节点连接
}
prevSibling = newFiber; // 记录前一个兄弟节点
index++;
}
}
关键实现细节说明:
alternate
属性关联新旧Fiber树dom: oldFiber!.dom
)child
和sibling
形成单向链表parent
指针实现effectTag
用于提交阶段确定需要执行的DOM操作nextUnitOfWork
实现可中断的协调过程需要补充的完整功能:
实现了React Fiber架构的核心协调机制,通过链表遍历和双缓冲技术实现了高效的可中断渲染流程。
scheduler.ts
// react/scheduler.ts
// React 调度器核心实现(基于requestIdleCallback)
import { Fiber, wipRoot, currentRoot, nextUnitOfWork } from './fiber';
import { performUnitOfWork } from './reconciler';
let workInProgressFiber: Fiber | null = null; // 当前正在处理的Fiber节点
let hookIndex = 0; // Hooks状态索引(用于函数组件)
/**
* 调度器工作循环(浏览器空闲时执行)
* @param deadline - 浏览器空闲回调参数(含剩余时间信息)
*/
export function workLoop(deadline: IdleDeadline) {
let shouldYield = false; // 是否需要让出主线程标志
// 持续处理Fiber任务直到:
// 1. 没有更多任务(nextUnitOfWork为空)
// 2. 浏览器需要重新渲染(剩余时间<1ms)
while (nextUnitOfWork && !shouldYield) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork); // 执行单个工作单元
shouldYield = deadline.timeRemaining() < 1; // 检查剩余时间
}
// 所有任务处理完成时提交更新
if (!nextUnitOfWork && wipRoot) {
commitRoot(); // 提交阶段:将效果应用到真实DOM
}
requestIdleCallback(workLoop); // 注册下一个空闲回调
}
// 启动调度器(立即执行首次调度)
requestIdleCallback(workLoop);
/**
* 提交阶段:将工作进度根节点的效果应用到DOM
*/
function commitRoot() {
commitWork(wipRoot!.child); // 从根节点的第一个子节点开始提交
currentRoot = wipRoot; // 更新当前根节点引用
wipRoot = null; // 清空工作进度根节点
}
/**
* 递归提交Fiber节点效果到DOM
* @param fiber - 当前要提交的Fiber节点
*/
function commitWork(fiber: Fiber | null) {
if (!fiber) return;
const parentFiber = fiber.parent!; // 获取父Fiber节点
const domParent = parentFiber.dom!; // 获取父DOM节点(必存在)
// 根据effectTag执行不同操作
if (fiber.effectTag === 'PLACEMENT' && fiber.dom != null) {
// 插入新节点
domParent.appendChild(fiber.dom);
} else if (fiber.effectTag === 'UPDATE' && fiber.dom != null) {
// 更新现有节点属性
updateDom(fiber.dom, fiber.alternate!.props, fiber.props);
} else if (fiber.effectTag === 'DELETION') {
// 删除节点(递归处理子节点)
commitDeletion(fiber, domParent);
}
// 递归提交子节点和兄弟节点
commitWork(fiber.child);
commitWork(fiber.sibling);
}
/**
* DOM属性更新器(处理事件监听和属性变更)
* @param dom - 要更新的DOM元素
* @param prevProps - 旧属性
* @param nextProps - 新属性
*/
function updateDom(dom: HTMLElement | Text, prevProps: any, nextProps: any) {
// 辅助函数:判断是否为事件属性
const isEvent = (key: string) => key.startsWith('on');
// 辅助函数:判断是否为普通属性(排除children和事件)
const isProperty = (key: string) => key !== 'children' && !isEvent(key);
// 辅助函数:判断属性值是否变化
const isNew = (key: string) => (prev: any, next: any) => prev[key] !== next[key];
// 移除旧事件监听
Object.keys(prevProps)
.filter(isEvent)
.forEach(name => {
const eventType = name.toLowerCase().substring(2); // 转换onClick为click
dom.removeEventListener(eventType, prevProps[name]);
});
// 添加新事件监听
Object.keys(nextProps)
.filter(isEvent)
.forEach(name => {
const eventType = name.toLowerCase().substring(2);
dom.addEventListener(eventType, nextProps[name]);
});
// 删除已变更的旧属性
Object.keys(prevProps)
.filter(isProperty)
.filter(isNew(prevProps, nextProps))
.forEach(name => {
(dom as any)[name] = undefined; // 清除旧属性
});
// 设置新属性
Object.keys(nextProps)
.filter(isProperty)
.filter(isNew(prevProps, nextProps))
.forEach(name => {
(dom as any)[name] = nextProps[name]; // 更新属性值
});
}
/**
* 递归删除节点及其子节点
* @param fiber - 要删除的Fiber节点
* @param domParent - 父DOM节点
*/
function commitDeletion(fiber: Fiber, domParent: HTMLElement | Text) {
if (fiber.dom) {
// 直接删除有DOM节点的Fiber
domParent.removeChild(fiber.dom);
} else if (fiber.child) {
// 递归删除子节点(针对没有DOM的组件节点)
commitDeletion(fiber.child, domParent);
}
}
调度器特性:
requestIdleCallback
的协作式调度提交阶段特点:
优化策略:
关键数据结构:
该实现展示了React 18+并发模式的核心调度逻辑,通过将渲染工作分解为可中断的小任务单元,实现了对浏览器主线程的精细控制。
hook.ts
// react/hook.ts
// React Hooks 核心实现(基于Fiber架构)
import { Fiber } from './fiber';
// Hooks 状态类型定义
export type HookState = {
queue: (() => void)[]; // 更新队列(存储待执行的更新函数)
pending: any; // 待处理的更新(用于批量处理)
memoizedState: any; // 记忆化状态值
next: HookState | null; // 下一个Hook的链表指针
deps?: any[]; // 依赖项数组(useEffect专用)
callback?: () => void; // 副作用回调函数(useEffect专用)
cleanup?: () => void; // 清理函数(useEffect专用)
};
// Hooks 上下文类型定义
export type HookContext = {
fiber: Fiber; // 当前渲染的Fiber节点
hookIndex: number; // 当前Hook的索引位置
};
// 全局状态:当前正在渲染的Fiber节点
export let currentlyRenderingFiber: Fiber | null = null;
/**
* useState 钩子实现
* @param initial - 状态初始值
* @returns [当前状态, 状态更新函数]
*/
export function useState<T>(initial: T): [T, (newState: T) => void] {
const fiber = currentlyRenderingFiber!; // 获取当前渲染的Fiber
const hook = getHook(); // 获取当前Hook实例(或创建新Hook)
// 初始化状态(仅在首次渲染时执行)
if (!hook.alternate) {
hook.memoizedState = initial;
}
// 定义状态更新函数
const setState = (action: T) => {
// 将更新推入队列(支持函数式更新)
hook.queue.push(() => {
hook.memoizedState = action; // 直接更新记忆化状态
});
rerender(fiber); // 触发重新渲染
};
return [hook.memoizedState, setState]; // 返回状态和更新器
}
/**
* useEffect 钩子实现
* @param callback - 副作用回调函数
* @param deps - 依赖项数组
*/
export function useEffect(callback: () => void | (() => void), deps: any[]) {
const fiber = currentlyRenderingFiber!;
const hook = getHook();
// 获取旧依赖项(首次渲染时为undefined)
const oldDeps = hook.deps;
// 深度比较依赖项变化
const hasChanged = !oldDeps || deps.some((dep, i) => dep !== oldDeps![i]);
if (hasChanged) {
// 依赖变化时:
hook.callback = callback; // 更新回调函数
hook.cleanup = undefined; // 重置清理函数
hook.deps = deps; // 存储新依赖项
}
}
/**
* 获取当前Hook实例(或创建新Hook)
* @returns 当前Fiber节点对应的Hook实例
*/
function getHook() {
const fiber = currentlyRenderingFiber!;
const hookIndex = fiber.hookIndex; // 获取当前Hook索引
// 获取当前索引位置的Hook(首次渲染时需要创建)
let hook = fiber.hooks![hookIndex];
if (!hook) {
// 创建新Hook实例
hook = {
memoizedState: null, // 初始状态为null
queue: [], // 初始化空更新队列
pending: null, // 初始无待处理更新
next: null, // 链表指针初始化
deps: null, // 初始无依赖项
callback: null, // 初始无回调
cleanup: null, // 初始无清理函数
};
fiber.hooks!.push(hook); // 将新Hook加入链表
} else {
fiber.hookIndex++; // 非首次渲染时递增索引
}
return hook;
}
/**
* 触发重新渲染(调度协调过程)
* @param fiber - 需要重新渲染的Fiber节点
*/
function rerender(fiber: Fiber) {
// 创建虚拟根节点(用于启动新协调过程)
const root = {
dom: fiber.dom, // 复用原容器DOM
props: fiber.props, // 复用原props
alternate: fiber, // 指向旧Fiber树(双缓冲)
};
nextUnitOfWork = root; // 设置新的工作单元起点
}
Hooks 链表管理:
fiber.hooks
数组维护函数组件的Hooks链表hookIndex
指针实现线性遍历next
指针形成单向链表状态持久化:
alternate
指针实现双缓冲技术memoizedState
queue
数组实现副作用系统:
cleanup
字段管理并发更新:
rerender
函数创建虚拟根节点启动新协调过程nextUnitOfWork
指针实现任务调度我们实现了React函数组件的核心工作原理,包括:
index.tsx
/** @jsx createElement */
import { createElement } from './react/jsx';
import { render } from './react/fiber';
import { useState } from './react/hook';
function Counter() {
const [state, setState] = useState(0);
return (
<div>
<h1>{state}</h1>
<button onClick={() => setState(state + 1)}>+1</button>
</div>
);
}
const element = <Counter />;
const container = document.getElementById('root');
if (container) {
render(element, container);
}
index.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Mini Reacttitle>
head>
<body>
<div id="root">div>
<script src="./dist/index.js">script>
body>
html>
npm install typescript ts-node webpack webpack-cli html-webpack-plugin --save-dev
typescript
: TypeScript 编译器ts-node
: 在 Node.js 中直接执行 TypeScript 代码webpack
: 模块打包工具webpack-cli
: Webpack 命令行工具html-webpack-plugin
: 自动生成 HTML 文件并注入打包后的脚本--save-dev
: 标记为开发依赖{
"compilerOptions": {
"target": "ESNext", // 编译目标为最新 ES 规范(支持最新语法特性)
"module": "ESNext", // 使用 ES 模块系统(与 Webpack 兼容)
"esModuleInterop": true, // 允许 CommonJS 和 ES 模块互操作(解决默认导入问题)
"outDir": "./dist", // 编译输出目录
"jsx": "react-jsx" // 启用 React JSX 编译(需 TypeScript 4.1+)
},
"include": ["./"] // 包含当前目录下所有文件(可根据需要调整)
}
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 引入 HTML 模板插件
module.exports = {
entry: './index.tsx', // 入口文件(React 应用主入口)
output: {
filename: 'bundle.js', // 输出文件名(打包后的 JS 文件)
path: __dirname, // 输出目录(当前目录,建议改为 './dist')
publicPath: '/', // 资源基础路径(用于开发服务器)
clean: true // 构建前清理输出目录(Webpack 5+ 特性)
},
resolve: {
extensions: ['.ts', '.tsx', '.js'], // 自动解析的扩展名(按顺序尝试)
alias: { // 路径别名配置(示例)
'@': path.resolve(__dirname, 'src/')
}
},
module: {
rules: [
{
test: /\.tsx?$/, // 匹配所有 .ts 和 .tsx 文件
use: 'ts-loader', // 使用 ts-loader 处理 TypeScript
exclude: /node_modules/, // 排除 node_modules 目录
include: [path.resolve(__dirname, "src")] // 仅处理 src 目录(可选优化)
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html', // 指定 HTML 模板路径
inject: 'body', // 脚本注入位置(body 底部)
hash: true, // 添加版本哈希(防止缓存)
minify: { // 生产环境压缩配置
removeComments: true,
collapseWhitespace: true
}
})
],
devtool: 'source-map', // 生成 source map(开发环境推荐)
devServer: { // 开发服务器配置
static: './dist', // 静态文件目录
port: 3000, // 开发服务器端口
hot: true, // 启用 HMR(热模块替换)
open: true // 自动打开浏览器
},
mode: process.env.NODE_ENV === 'production' ? 'production' : 'development' // 环境判断
};
参考:【webpack】Webpack 实战配置教程(最全版):涵盖所有配置项 + 性能优化建议
TypeScript 配置亮点:
ESNext
目标确保使用最新 JavaScript 特性esModuleInterop
解决 import/require
兼容问题"jsx": "react-jsx"
以支持 JSX 编译Webpack 核心配置:
index.tsx
开始构建,输出到 dist
目录.ts
/.tsx
文件,排除 node_modules
环境区分:
devtool: 'eval-cheap-module-source-map'
mode: 'production'
+ 代码分割配置建议大家根据实际项目需求调整路径和插件配置,此配置已包含 React 开发所需的基本功能。
模块 | 内容 |
---|---|
JSX 解析 | 自定义 createElement 处理 JSX |
Virtual DOM | 用于描述组件树 |
Fiber 架构 | 支持递归构建 Fiber 树 |
协调器 | Diff 算法处理新增/更新/删除 |
调度器 | 利用 requestIdleCallback 实现时间切片 |
Hook 系统 | 支持 useState 和 useEffect 的基础逻辑 |
参考源码笔记:
【React源码01】深入学习React 源码实现——JSX与虚拟DOM生成
【React源码02】深入学习React 源码实现——虚拟 DOM(Virtual DOM)与 Diffing 算法
【React源码03】深入学习React 源码实现——Fiber 架构与任务调度
【React源码04】深入学习React 源码实现—— React Hooks 的底层实现原理(useState,useEffect,useRef)
【React源码08】深入学习React 源码实现——Fiber架构双缓冲(current 与 workInProgress)机制
【React源码10】深入学习React 源码实现——优先级Lane模型
【React源码11】深入学习React 源码实现——调度器(Scheduler)底层实现
【React源码12】深入学习React 源码实现——Scheduler 与 Fiber 的协同工作机制
【React源码13】深入学习React 源码实现——如何自定义 React 的调度策略(Custom Scheduler Strategy)
方向 | 建议 |
---|---|
支持函数组件 | 增加对 renderWithHooks 的封装 |
支持 Effect 清理 | 在 commit 阶段执行 cleanup 函数 |
支持 Hooks 异常边界 | 类似 getDerivedStateFromError |
支持并发模式 | 引入 Lane 优先级系统 |
支持 Suspense | 实现异步加载状态管理 |
支持 Context API | 实现上下文传递机制 |
参考源码笔记:
【React源码09】深入学习React 源码实现——Fiber架构副作用收集Effect List
【React源码32】深入学习React 源码实现——React 错误边界(Error Boundaries)与异常捕获机制
【React源码06】深入学习React 源码实现—— Context API底层实现
【React源码26】深入学习React 源码实现——React.lazy 与 Suspense 的底层实现
通过我们本次实战演练,大家已经掌握了 React 最核心的底层原理和实现方式。尽管这是一个极简版本,但它完整展示了:
这套知识体系是理解 React 源码、提升前端架构能力的重要一步。
如果你希望继续深入学习,可以参考老曹的源码系列文章,尝试:
useEffect
清理逻辑;useReducer
、useRef
;Suspense
;在此,我们的react源码第一阶段学习到此完美收官,祝你在 React 源码探索之路上越走越远!