# 🌵 React
# 001:React Fiber 是什么
Fiber 结构:
- 每个 Fiber 节点对应一个 React 元素(如组件实例、DOM 节点等)
- 每个 Fiber 节点有一个或多个子节点(子树),有一个或多个兄弟节点(兄弟树)
Fiber 工作原理
采用 Fiber 原因
React 采用 Fiber 架构的原因是 JavaScript 的运行会阻塞页面的渲染,React 为了不阻塞页面的渲染,采用了 Fiber 架构,Fiber 也是一种单向链表的数据结构,基于这个数据结构可以实现由原来不可中断的更新过程变成异步的可中断的更新。
在 16 之前,React 是直接递归渲染 vdom 的,setState 会触发重新渲染,对比渲染出的新旧 vdom,对差异部分进行 dom 操作。
在 16 之后,为了优化性能,会先把 vdom 转换成 fiber,也就是从树转换成链表,然后再渲染。整体渲染流程分成了两个阶段:
1)render 阶段:从 vdom 转换成 fiber,并且对需要 dom 操作的节点打上 effectTag 的标记
2)commit 阶段:对有 effectTag 标记的 fiber 节点进行 dom 操作,并执行所有的 effect 副作用函数。
# 数组 children 的 O(n) Diff(简化伪代码)
function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren) {
// 1) 建立旧 children 的遍历引用
let oldFiber = currentFirstChild; // 旧链表
let lastPlacedIndex = 0; // 已“就位”的最大旧索引
const existingByKey = new Map(); // 备用:后半段把旧 children 按 key 缓存
// 2) 先同索引线性对比,遇到 key/type 不同则跳出,进入 map 阶段
for (let newIndex = 0; newIndex < newChildren.length && oldFiber != null; newIndex++) {
const newChild = newChildren[newIndex];
if (!sameKeyAndType(oldFiber, newChild)) break;
const newFiber = useOrCreateFiber(oldFiber, newChild);
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIndex);
link(returnFiber, newFiber); // 串联 child/sibling
oldFiber = oldFiber.sibling;
}
// 3) 将剩余旧 children 建 key→fiber 的映射
while (oldFiber != null) {
existingByKey.set(oldFiber.key ?? oldFiber.index, oldFiber);
oldFiber = oldFiber.sibling;
}
// 4) 遍历剩余新 children,按 key 在映射中查找并复用,否则创建;同时判定是否需要移动/插入
for (let newIndex = /* 继续 */; newIndex < newChildren.length; newIndex++) {
const newChild = newChildren[newIndex];
const match = existingByKey.get(newChild.key ?? newIndex) || null;
const newFiber = useOrCreateFiber(match, newChild);
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIndex);
link(returnFiber, newFiber);
if (match) existingByKey.delete(newChild.key ?? newIndex);
}
// 5) 映射中剩余的旧 fiber 全部删除
existingByKey.forEach(f => deleteChild(returnFiber, f));
}
function placeChild(newFiber, lastPlacedIndex, newIndex) {
newFiber.index = newIndex;
const current = newFiber.alternate; // 旧 fiber
if (current !== null) {
const oldIndex = current.index;
// 旧索引落后于已就位的最大索引 → 需要移动(标记 Placement)
if (oldIndex < lastPlacedIndex) {
newFiber.flags |= Placement;
return lastPlacedIndex; // 不更新
} else {
return oldIndex; // 更新最大已就位索引
}
} else {
// 新创建的节点必然是插入
newFiber.flags |= Placement;
return lastPlacedIndex;
}
}