前言
在现代前端开发中,React 无疑是最受欢迎的框架之一。然而,许多开发者只是停留在使用层面,对其底层原理知之甚少。今天,我们将一起动手实现一个简易版的 React,重点解析其核心的 Fiber 架构和协调算法。通过这个实践,你不仅能深入理解 React 的工作原理,还能提升自己的 JavaScript 和算法能力。
一、React 核心架构演进
在深入实现之前,我们先了解一下 React 架构的演进历程:
- Stack Reconciler(React 15 及之前):使用递归方式遍历虚拟 DOM,更新过程是同步且不可中断的
- Fiber Reconciler(React 16+):基于 Fiber 节点的链表结构,支持增量渲染和优先级调度
Fiber 架构的核心优势在于:
- 可中断的渲染过程
- 优先级调度
- 更好的错误边界处理
- 并发模式的基础
二、实现简易虚拟 DOM
首先,让我们定义最基本的虚拟 DOM 结构:
// 虚拟DOM节点类
class VNode {
constructor(type, props, children) {
this.type = type; // 标签类型,如 'div', 'span' 或函数组件
this.props = props || {}; // 属性对象
this.children = children || []; // 子节点
this.key = props?.key; // 用于diff算法的key
this.dom = null; // 对应的真实DOM
}
}
// 创建虚拟DOM的辅助函数
function createElement(type, props, ...children) {
// 扁平化children并过滤null/undefined
const flatChildren = children.flat(Infinity).filter(child =>
child != null && child !== false && child !== true
);
return new VNode(type, props, flatChildren);
}
三、Fiber 节点设计
Fiber 是 React 16 引入的核心数据结构,我们将实现一个简化版本:
class FiberNode {
constructor(vnode) {
this.type = vnode.type; // 节点类型
this.props = vnode.props; // 属性
this.key = vnode.key; // key值
// 链表结构
this.child = null; // 第一个子fiber
this.sibling = null; // 下一个兄弟fiber
this.parent = null; // 父fiber
this.alternate = null; // 上一次渲染的fiber(用于diff)
// 渲染相关
this.stateNode = null; // 对应的真实DOM或组件实例
this.effectTag = null; // 标记需要进行的操作(PLACEMENT, UPDATE, DELETION)
this.firstEffect = null; // 第一个需要处理的副作用
this.lastEffect = null; // 最后一个需要处理的副作用
// 用于函数组件
this.hooks = []; // 存储hooks
this.hookIndex = 0; // 当前hook索引
}
}
四、协调算法(Reconciliation)实现
协调算法是 React 的核心,负责比较新旧虚拟 DOM 并找出最小更新操作:
class Reconciler {
constructor() {
this.nextUnitOfWork = null; // 下一个工作单元
this.currentRoot = null; // 当前渲染的根fiber
this.wipRoot = null; // 正在构建的根fiber
this.deletions = []; // 需要删除的节点
}
// 开始渲染
render(element, container) {
this.wipRoot = new FiberNode({
type: 'ROOT',
stateNode: container,
props: { children: [element] }
});
this.nextUnitOfWork = this.wipRoot;
// 启动工作循环
requestIdleCallback(this.workLoop.bind(this));
}
// 工作循环(模拟React的调度器)
workLoop(deadline) {
let shouldYield = false;
while (this.nextUnitOfWork && !shouldYield) {
this.nextUnitOfWork = this.performUnitOfWork(this.nextUnitOfWork);
shouldYield = deadline.timeRemaining() < 1;
}
if (!this.nextUnitOfWork && this.wipRoot) {
this.commitRoot();
}
if (this.nextUnitOfWork) {
requestIdleCallback(this.workLoop.bind(this));
}
}
// 执行单个工作单元
performUnitOfWork(fiber) {
// 1. 创建当前fiber的DOM或组件实例
if (!fiber.stateNode) {
fiber.stateNode = this.createDom(fiber);
}
// 2. 协调子元素
const elements = fiber.props?.children || [];
this.reconcileChildren(fiber, elements);
// 3. 返回下一个工作单元
if (fiber.child) {
return fiber.child;
}
let nextFiber = fiber;
while (nextFiber) {
if (nextFiber.sibling) {
return nextFiber.sibling;
}
nextFiber = nextFiber.parent;
}
return null;
}
// 协调子节点
reconcileChildren(wipFiber, elements) {
let index = 0;
let oldFiber = wipFiber.alternate?.child;
let prevSibling = null;
while (index < elements.length || oldFiber) {
const element = elements[index];
let newFiber = null;
// 比较新旧fiber
const sameType = oldFiber && element &&
element.type === oldFiber.type &&
element.key === oldFiber.key;
if (sameType) {
// 类型相同,更新属性
newFiber = new FiberNode(element);
newFiber.stateNode = oldFiber.stateNode;
newFiber.alternate = oldFiber;
newFiber.effectTag = 'UPDATE';
newFiber.parent = wipFiber;
}
if (element && !sameType) {
// 类型不同,创建新节点
newFiber = new FiberNode(element);
newFiber.effectTag = 'PLACEMENT';
newFiber.parent = wipFiber;
}
if (oldFiber && !sameType) {
// 删除旧节点
oldFiber.effectTag = 'DELETION';
this.deletions.push(oldFiber);
}
if (oldFiber) {
oldFiber = oldFiber.sibling;
}
if (newFiber) {
if (index === 0) {
wipFiber.child = newFiber;
} else {
prevSibling.sibling = newFiber;
}
prevSibling = newFiber;
}
index++;
}
}
// 创建真实DOM
createDom(fiber) {
if (typeof fiber.type === 'function') {
// 处理函数组件
return this.updateFunctionComponent(fiber);
}
const dom = fiber.type === 'TEXT_ELEMENT'
? document.createTextNode('')
: document.createElement(fiber.type);
// 更新DOM属性
this.updateDomProperties(dom, {}, fiber.props);
return dom;
}
// 更新函数组件
updateFunctionComponent(fiber) {
fiber.hooks = [];
fiber.hookIndex = 0;
const children = [fiber.type(fiber.props)];
this.reconcileChildren(fiber, children);
return null;
}
// 提交所有变更到真实DOM
commitRoot() {
this.deletions.forEach(this.commitWork.bind(this));
this.commitWork(this.wipRoot.child);
this.currentRoot = this.wipRoot;
this.wipRoot = null;
this.deletions = [];
}
// 提交单个fiber的变更
commitWork(fiber) {
if (!fiber) return;
let domParentFiber = fiber.parent;
while (!domParentFiber.stateNode) {
domParentFiber = domParentFiber.parent;
}
const domParent = domParentFiber.stateNode;
if (fiber.effectTag === 'PLACEMENT' && fiber.stateNode) {
domParent.appendChild(fiber.stateNode);
} else if (fiber.effectTag === 'UPDATE' && fiber.stateNode) {
this.updateDomProperties(
fiber.stateNode,
fiber.alternate.props,
fiber.props
);
} else if (fiber.effectTag === 'DELETION') {
this.commitDeletion(fiber, domParent);
}
this.commitWork(fiber.child);
this.commitWork(fiber.sibling);
}
// 更新DOM属性
updateDomProperties(dom, prevProps, nextProps) {
// 移除旧的属性
Object.keys(prevProps).forEach