手写 React 高质量源码开发需要深入理解 React 的核心机制(如虚拟 DOM、协调算法、Hooks 等)。以下是分步骤实现一个简化版 React 的指南,包含核心设计思路和代码示例:
一、核心架构设计
1. 模块划分
markdown
复制
- **React Core** : 虚拟DOM、组件生命周期、Hooks
- **Reconciler** : Fiber架构、Diff算法
- **Renderer** : DOM操作(浏览器端)
- **Scheduler** : 任务调度(时间切片、优先级)
2. 技术栈选择
javascript
复制
// 开发环境
- TypeScript(类型安全)
- Rollup(打包库)
- Jest + Testing Library(单元测试)
// 核心依赖
- requestIdleCallback(模拟调度器)
- MutationObserver(DOM变化监听)
二、实现核心模块
1. 虚拟DOM与JSX转换
typescript
复制
// 实现 createElement(处理 JSX 转换)
function createElement(
type: string | Function,
props: Record<string, any>,
...children: any[]
): VNode {
return {
type,
props: {
...props,
children: children.flat().map(child =>
typeof child === "object" ? child : createTextElement(child)
),
},
};
}
// 文本节点处理
function createTextElement(text: string): VNode {
return {
type: "TEXT_ELEMENT",
props: {
nodeValue: text,
children: [],
},
};
}
2. 渲染引擎(Reconciliation)
typescript
复制
// Fiber节点结构
interface Fiber {
type?: string | Function;
dom?: HTMLElement | Text;
parent?: Fiber;
child?: Fiber;
sibling?: Fiber;
alternate?: Fiber; // 用于Diff
effectTag?: "PLACEMENT" | "UPDATE" | "DELETION";
props: {
children: Fiber[];
[key: string]: any;
};
hooks?: any[]; // Hooks存储
}
// Diff算法核心
function reconcileChildren(wipFiber: Fiber, elements: Fiber[]) {
let index = 0;
let oldFiber = wipFiber.alternate?.child;
let prevSibling: Fiber | null = null;
while (index < elements.length || oldFiber) {
const element = elements[index];
let newFiber: Fiber | null = null;
// 类型比较
const sameType = oldFiber && element && element.type === oldFiber.type;
if (sameType) {
// 更新节点
newFiber = {
type: oldFiber!.type,
props: element.props,
dom: oldFiber!.dom,
parent: wipFiber,
alternate: oldFiber,
effectTag: "UPDATE",
};
}
if (!sameType && element) {
// 创建新节点
newFiber = {
type: element.type,
props: element.props,
dom: null,
parent: wipFiber,
alternate: null,
effectTag: "PLACEMENT",
};
}
if (oldFiber && !sameType) {
// 删除旧节点
oldFiber.effectTag = "DELETION";
deletions.push(oldFiber);
}
// 构建链表结构
if (index === 0) {
wipFiber.child = newFiber!;
} else if (element) {
prevSibling!.sibling = newFiber!;
}
prevSibling = newFiber;
oldFiber = oldFiber?.sibling;
index++;
}
}
3. Hooks实现
typescript
复制
let currentHookFiber: Fiber | null = null;
let hookIndex = 0;
// useState 实现
function useState<T>(initial: T): [T, (action: T | ((prev: T) => T)) => void] {
const oldHook = currentHookFiber?.alternate?.hooks?.[hookIndex];
const hook = {
state: oldHook ? oldHook.state : initial,
queue: [] as ((prev: T) => T)[],
};
// 批量更新处理
const actions = oldHook ? oldHook.queue : [];
actions.forEach(action => {
hook.state = typeof action === "function"
? (action as Function)(hook.state)
: action;
});
const setState = (action: T | ((prev: T) => T)) => {
hook.queue.push(action);
// 触发重新渲染
scheduleRerender();
};
currentHookFiber!.hooks!.push(hook);
hookIndex++;
return [hook.state, setState];
}
// useEffect 实现
function useEffect(callback: () => void, deps?: any[]) {
const hook = {
deps,
cleanup: undefined as (() => void) | undefined,
};
const oldHook = currentHookFiber?.alternate?.hooks?.[hookIndex];
const hasChanged = !oldHook ||
deps?.some((dep, i) => dep !== oldHook.deps?.[i]);
if (hasChanged) {
setTimeout(() => {
hook.cleanup = callback();
});
} else {
hook.cleanup = oldHook.cleanup;
}
currentHookFiber!.hooks!.push(hook);
hookIndex++;
}
三、性能优化关键点
1. 时间切片(Time Slicing)
typescript
复制
// 基于 requestIdleCallback 的任务调度
function workLoop(deadline: IdleDeadline) {
let shouldYield = false;
while (nextUnitOfWork && !shouldYield) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
shouldYield = deadline.timeRemaining() < 1;
}
if (!nextUnitOfWork && wipRoot) {
commitRoot();
}
requestIdleCallback(workLoop);
}
2. 差异更新策略
typescript
复制
// DOM属性差异比较
function updateDom(
dom: HTMLElement | Text,
prevProps: Record<string, any>,
nextProps: Record<string, any>
) {
// 删除旧属性
Object.keys(prevProps)
.filter(isProperty)
.filter(key => !(key in nextProps))
.forEach(name => {
(dom as any)[name] = "";
});
// 设置新属性
Object.keys(nextProps)
.filter(isProperty)
.filter(key => prevProps[key] !== nextProps[key])
.forEach(name => {
(dom as any)[name] = nextProps[name];
});
// 事件处理
Object.keys(prevProps)
.filter(isEvent)
.filter(key => !(key in nextProps) || prevProps[key] !== nextProps[key])
.forEach(name => {
const eventType = name.toLowerCase().substring(2);
dom.removeEventListener(eventType, prevProps[name]);
});
Object.keys(nextProps)
.filter(isEvent)
.filter(key => prevProps[key] !== nextProps[key])
.forEach(name => {
const eventType = name.toLowerCase().substring(2);
dom.addEventListener(eventType, nextProps[name]);
});
}
四、测试策略
1. 单元测试示例(Jest)
typescript
复制
test('useState should update state', () => {
function TestComponent() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
const container = document.createElement('div');
ReactDOM.render(<TestComponent />, container);
const button = container.querySelector('button')!;
expect(button.textContent).toBe('0');
button.click();
expect(button.textContent).toBe('1');
});
2. 性能测试
javascript
复制
// 使用 Benchmark.js 测试渲染性能
suite('Virtual DOM vs Real DOM', () => {
benchmark('Real DOM update', () => {
const div = document.createElement('div');
for (let i = 0; i < 1000; i++) {
const span = document.createElement('span');
span.textContent = i;
div.appendChild(span);
}
});
benchmark('Virtual DOM update', () => {
const elements = [];
for (let i = 0; i < 1000; i++) {
elements.push(<span>{i}</span>);
}
ReactDOM.render(<div>{elements}</div>, document.createElement('div'));
});
});
五、开发注意事项
- 内存管理:及时清理不再使用的 Fiber 节点
- 异常边界:实现 componentDidCatch 类似机制
- SSR支持:增加 hydrate 方法处理服务端渲染
- Type Safety:严格类型校验避免运行时错误
- Tree Shaking:模块化设计支持按需加载
通过实现这些核心模块,可以深入理解 React 的底层原理。建议从简化版开始(约 500 行代码),逐步添加功能模块。参考 React 官方源码时注意:
- 阅读
react/packages目录下的独立模块 - 使用调试工具分析调度流程
- 重点关注
react-reconciler和scheduler包 - 使用
__DEV__标志学习开发模式下的警告机制
最终实现的迷你 React 应具备:虚拟 DOM Diff、函数组件、Hooks、异步渲染等核心功能。