跳到主要内容

React Fiber 树反射

负责处理 Fiber 树的反射机制 ,即通过 Fiber 节点的引用关系反向查找其对应的组件类型、 DOM 节点等信息。

一、获取最近的已挂载的 Fiber

export function getNearestMountedFiber(fiber: Fiber): null | Fiber {
let node = fiber;
let nearestMounted: null | Fiber = fiber;
if (!fiber.alternate) {
// If there is no alternate, this might be a new tree that isn't inserted
// 如果没有备用节点,这可能是一个尚未插入的新树。
// yet. If it is, then it will have a pending insertion effect on it.
// 如果已经插入,则它将有一个待处理的插入效果。
let nextNode: Fiber = node;
do {
node = nextNode;
if ((node.flags & (Placement | Hydrating)) !== NoFlags) {
// This is an insertion or in-progress hydration. The nearest possible
// 这是一个插入或正在进行的水合操作。
// mounted fiber is the parent but we need to continue to figure out
// 最近可能的已挂载 fiber 是父节点,但我们需要继续确认它是否仍然挂载。
// if that one is still mounted.
nearestMounted = node.return;
}
nextNode = node.return;
} while (nextNode);
} else {
while (node.return) {
node = node.return;
}
}
if (node.tag === HostRoot) {
// TODO: Check if this was a nested HostRoot when used with
// renderContainerIntoSubtree.
// 待办事项:检查在使用 renderContainerIntoSubtree 时这是否是嵌套的 HostRoot。
return nearestMounted;
}
// If we didn't hit the root, that means that we're in an disconnected tree
// that has been unmounted.
// 如果我们没有命中根节点,那就意味着我们处在一个已经被卸载的孤立树中。
return null;
}

二、从 Fiber 获取 Suspense 实例

export function getSuspenseInstanceFromFiber(
fiber: Fiber,
): null | SuspenseInstance {
if (fiber.tag === SuspenseComponent) {
let suspenseState: SuspenseState | null = fiber.memoizedState;
if (suspenseState === null) {
const current = fiber.alternate;
if (current !== null) {
suspenseState = current.memoizedState;
}
}
if (suspenseState !== null) {
return suspenseState.dehydrated;
}
}
return null;
}

三、从Fiber获取活动实例

export function getActivityInstanceFromFiber(
fiber: Fiber,
): null | ActivityInstance {
if (fiber.tag === ActivityComponent) {
let activityState: ActivityState | null = fiber.memoizedState;
if (activityState === null) {
const current = fiber.alternate;
if (current !== null) {
activityState = current.memoizedState;
}
}
if (activityState !== null) {
return activityState.dehydrated;
}
}
// TODO: Implement this on ActivityComponent.
// 待办:在 ActivityComponent 上实现此功能。
return null;
}

四、从 Fiber 获取容器

export function getContainerFromFiber(fiber: Fiber): null | Container {
return fiber.tag === HostRoot
? (fiber.stateNode.containerInfo: Container)
: null;
}

五、使用慢路径查找当前 Fiber

// 断言是否已挂载
function assertIsMounted(fiber: Fiber) {
if (getNearestMountedFiber(fiber) !== fiber) {
throw new Error('Unable to find node on an unmounted component.');
}
}

export function findCurrentFiberUsingSlowPath(fiber: Fiber): Fiber | null {
const alternate = fiber.alternate;
if (!alternate) {
// If there is no alternate, then we only need to check if it is mounted.
// 如果没有备用项,那么我们只需要检查它是否已挂载。
const nearestMounted = getNearestMountedFiber(fiber);

if (nearestMounted === null) {
throw new Error('Unable to find node on an unmounted component.');
}

if (nearestMounted !== fiber) {
return null;
}
return fiber;
}
// If we have two possible branches, we'll walk backwards up to the root
// 如果我们有两个可能的分支,我们将向上回溯到根节点
// to see what path the root points to. On the way we may hit one of the
// 以查看根节点指向的路径。在这个过程中,我们可能会
// special cases and we'll deal with them.
// 遇到一些特殊情况,届时我们会处理它们。
let a: Fiber = fiber;
let b: Fiber = alternate;
while (true) {
const parentA = a.return;
if (parentA === null) {
// We're at the root.
break;
}
const parentB = parentA.alternate;
if (parentB === null) {
// There is no alternate. This is an unusual case. Currently, it only
// 没有备用方案。这是一个不寻常的情况。
// happens when a Suspense component is hidden. An extra fragment fiber
// 目前,它只会在 Suspense 组件被隐藏时发生。
// is inserted in between the Suspense fiber and its children. Skip
// 一个额外的片段 fiber 会插入在 Suspense fiber 与其子节点之间。
// over this extra fragment fiber and proceed to the next parent.
// 跳过这个额外的片段 fiber,继续处理下一个父节点。
const nextParent = parentA.return;
if (nextParent !== null) {
a = b = nextParent;
continue;
}
// If there's no parent, we're at the root.
// 如果没有父节点,我们就在根节点。
break;
}

// If both copies of the parent fiber point to the same child, we can
// 如果父 fiber 的两个副本都指向相同的子节点,我们可以
// assume that the child is current. This happens when we bailout on low
// 假设该子节点是当前的。这发生在低优先级的情况下中断执行时:
// priority: the bailed out fiber's child reuses the current child.
// 被中断的 fiber 的子节点重用了当前的子节点。
if (parentA.child === parentB.child) {
let child = parentA.child;
while (child) {
if (child === a) {
// We've determined that A is the current branch.
// 我们已经确定 A 是当前分支。
assertIsMounted(parentA);
return fiber;
}
if (child === b) {
// We've determined that B is the current branch.
// 我们已经确定 B 是当前分支。
assertIsMounted(parentA);
return alternate;
}
child = child.sibling;
}

// We should never have an alternate for any mounting node. So the only
// 我们绝不应该为任何挂载节点设置替代节点。
// way this could possibly happen is if this was unmounted, if at all.
// 所以这种情况唯一可能发生的情况是,如果它被卸载的话。
throw new Error('Unable to find node on an unmounted component.');
}

if (a.return !== b.return) {
// The return pointer of A and the return pointer of B point to different
// A 的返回指针和 B 的返回指针指向不同的协程。
// fibers. We assume that return pointers never criss-cross, so A must
// 我们假设返回指针从不交叉,因此 A
// belong to the child set of A.return, and B must belong to the child
// 必须属于 A.return 的子集,而 B 必须属于 B.return 的子集。
// set of B.return.
a = parentA;
b = parentB;
} else {
// The return pointers point to the same fiber. We'll have to use the
// 返回指针指向同一个纤程。
// default, slow path: scan the child sets of each parent alternate to see
// 我们将不得不使用默认的慢路径:扫描每个父节点备用子集,看看哪个子节点属于哪个集合。
// which child belongs to which set.
//
// Search parent A's child set
// 搜索父节点 A 的子集
let didFindChild = false;
let child = parentA.child;
while (child) {
if (child === a) {
didFindChild = true;
a = parentA;
b = parentB;
break;
}
if (child === b) {
didFindChild = true;
b = parentA;
a = parentB;
break;
}
child = child.sibling;
}
if (!didFindChild) {
// Search parent B's child set
// 搜索父B的子集合
child = parentB.child;
while (child) {
if (child === a) {
didFindChild = true;
a = parentB;
b = parentA;
break;
}
if (child === b) {
didFindChild = true;
b = parentB;
a = parentA;
break;
}
child = child.sibling;
}

if (!didFindChild) {
throw new Error(
'Child was not found in either parent set. This indicates a bug ' +
'in React related to the return pointer. Please file an issue.',
);
}
}
}

if (a.alternate !== b) {
throw new Error(
"Return fibers should always be each others' alternates. " +
'This error is likely caused by a bug in React. Please file an issue.',
);
}
}

// If the root is not a host container, we're in a disconnected tree. I.e.
// unmounted.
// 如果根节点不是宿主容器,我们就在一个断开连接的树中。也就是 // 已卸载的状态。
if (a.tag !== HostRoot) {
throw new Error('Unable to find node on an unmounted component.');
}

if (a.stateNode.current === a) {
// We've determined that A is the current branch.
// 我们已经确定 A 是当前分支。
return fiber;
}
// Otherwise B has to be current branch.
// 否则 B 必须是当前分支。
return alternate;
}

六、获取当前的宿主的 fiber

信息

部分常量出自于 ReactWorkTags

export function findCurrentHostFiber(parent: Fiber): Fiber | null {
// 使用慢路径查找当前 Fiber
const currentParent = findCurrentFiberUsingSlowPath(parent);
return currentParent !== null
? findCurrentHostFiberImpl(currentParent)
: null;
}
// 获取当前宿主 Fiber 的实现方法
function findCurrentHostFiberImpl(node: Fiber): Fiber | null {
// Next we'll drill down this component to find the first HostComponent/Text.
// 接下来我们将深入这个组件,找到第一个 HostComponent/Text。
const tag = node.tag;
if (
// HostComponent 在 ReactWorkTags 文件中,值为 5
tag === HostComponent ||
// HostHoistable 在 ReactWorkTags 文件中,值为 26
tag === HostHoistable ||
// HostSingleton 在 ReactWorkTags 文件中,值为 27
tag === HostSingleton ||
// HostText 在 ReactWorkTags 文件中,值为 6
tag === HostText
) {
return node;
}

let child = node.child;
while (child !== null) {
const match = findCurrentHostFiberImpl(child);
if (match !== null) {
return match;
}
child = child.sibling;
}

return null;
}

七、查找没有入口的当前宿主 Fiber

信息

部分常量出自于 ReactWorkTags

export function findCurrentHostFiberWithNoPortals(parent: Fiber): Fiber | null {
const currentParent = findCurrentFiberUsingSlowPath(parent);
return currentParent !== null
? findCurrentHostFiberWithNoPortalsImpl(currentParent)
: null;
}

// 查找没有门户的当前主机 Fiber 实现
function findCurrentHostFiberWithNoPortalsImpl(node: Fiber): Fiber | null {
// Next we'll drill down this component to find the first HostComponent/Text.
// 接下来我们将深入这个组件,找到第一个 HostComponent/Text。
const tag = node.tag;
if (
// HostComponent 在 ReactWorkTags 文件中,值为 5
tag === HostComponent ||
// HostHoistable 在 ReactWorkTags 文件中,值为 26
tag === HostHoistable ||
// HostSingleton 在 ReactWorkTags 文件中,值为 27
tag === HostSingleton ||
// HostText 在 ReactWorkTags 文件中,值为 6
tag === HostText
) {
return node;
}

let child = node.child;
while (child !== null) {
// HostPortal 在 ReactWorkTags 文件中,值为 4
if (child.tag !== HostPortal) {
const match = findCurrentHostFiberWithNoPortalsImpl(child);
if (match !== null) {
return match;
}
}
child = child.sibling;
}

return null;
}

八、是否为 Fiber Suspense 且已超时

export function isFiberSuspenseAndTimedOut(fiber: Fiber): boolean {
const memoizedState = fiber.memoizedState;
return (
// HostPortal 在 ReactWorkTags 文件中,值为 13
fiber.tag === SuspenseComponent &&
memoizedState !== null &&
memoizedState.dehydrated === null
);
}

九、是否含有 Fiber 容器

export function doesFiberContain(
parentFiber: Fiber,
childFiber: Fiber,
): boolean {
let node: null | Fiber = childFiber;
const parentFiberAlternate = parentFiber.alternate;
while (node !== null) {
if (node === parentFiber || node === parentFiberAlternate) {
return true;
}
node = node.return;
}
return false;
}

十、遍历片段实例

export function traverseFragmentInstance<A, B, C>(
fragmentFiber: Fiber,
fn: (Fiber, A, B, C) => boolean,
a: A,
b: B,
c: C,
): void {
// 工具 - 1
traverseVisibleHostChildren(fragmentFiber.child, false, fn, a, b, c);
}

十一、深度遍历片段实例

export function traverseFragmentInstanceDeeply<A, B, C>(
fragmentFiber: Fiber,
fn: (Fiber, A, B, C) => boolean,
a: A,
b: B,
c: C,
): void {
// 工具 - 1
traverseVisibleHostChildren(fragmentFiber.child, true, fn, a, b, c);
}

十二、获取 Fragment 父宿主 Fiber

export function getFragmentParentHostFiber(fiber: Fiber): null | Fiber {
let parent = fiber.return;
while (parent !== null) {
if (parent.tag === HostRoot || parent.tag === HostComponent) {
return parent;
}
parent = parent.return;
}

return null;
}

十三、Fibers 已传输到宿主

export function fiberIsPortaledIntoHost(fiber: Fiber): boolean {
let foundPortalParent = false;
let parent = fiber.return;
while (parent !== null) {
if (parent.tag === HostPortal) {
foundPortalParent = true;
}
if (parent.tag === HostRoot || parent.tag === HostComponent) {
break;
}
parent = parent.return;
}
return foundPortalParent;
}

十四、获取Fragment实例的兄弟组件

export function getFragmentInstanceSiblings(
fiber: Fiber,
): [Fiber | null, Fiber | null] {
const result: [Fiber | null, Fiber | null] = [null, null];
// 十二、获取 Fragment 父宿主 Fiber
const parentHostFiber = getFragmentParentHostFiber(fiber);
if (parentHostFiber === null) {
return result;
}

findFragmentInstanceSiblings(result, fiber, parentHostFiber.child);
return result;
}

function findFragmentInstanceSiblings(
result: [Fiber | null, Fiber | null],
self: Fiber,
child: null | Fiber,
foundSelf: boolean = false,
): boolean {
while (child !== null) {
if (child === self) {
foundSelf = true;
if (child.sibling) {
child = child.sibling;
} else {
return true;
}
}
// HostComponent 在 ReactWorkTags 文件中,值为 5
if (child.tag === HostComponent) {
if (foundSelf) {
result[1] = child;
return true;
} else {
result[0] = child;
}
} else if (
// OffscreenComponent 在 ReactWorkTags 文件中,值为 22
child.tag === OffscreenComponent &&
child.memoizedState !== null
) {
// Skip hidden subtrees
// 跳过隐藏子树
} else {
if (findFragmentInstanceSiblings(result, self, child.child, foundSelf)) {
return true;
}
}
child = child.sibling;
}
return false;
}

十五、 从主机 Fiber 获取实例

export function getInstanceFromHostFiber<I>(fiber: Fiber): I {
switch (fiber.tag) {
case HostComponent:
return fiber.stateNode;
case HostRoot:
return fiber.stateNode.containerInfo;
default:
throw new Error('Expected to find a host node. This is a bug in React.');
}
}

十六、获取下一个兄弟宿主 Fiber

export function getNextSiblingHostFiber(fiber: Fiber): null | Fiber {
// 该方法在 工具 - 1
traverseVisibleHostChildren(fiber.sibling, false, findNextSibling);
const sibling = popSearchTarget();
// 该方法在 常数 - 1
pushSearchTarget(null);
return sibling;
}
// 查找下一个兄弟
function findNextSibling(child: Fiber): boolean {
pushSearchTarget(child);
return true;
}

十七、 Fiber 是否被片段包含

export function isFiberContainedByFragment(
fiber: Fiber,
fragmentFiber: Fiber,
): boolean {
let current: Fiber | null = fiber;
while (current !== null) {
if (
// Fragment 在 ReactWorkTags 文件中,值为 7
current.tag === Fragment &&
(current === fragmentFiber || current.alternate === fragmentFiber)
) {
return true;
}
current = current.return;
}
return false;
}

十八、片段是否被 Fiber 包含

export function isFragmentContainedByFiber(
fragmentFiber: Fiber,
otherFiber: Fiber,
): boolean {
let current: Fiber | null = fragmentFiber;
const fiberHostParent: Fiber | null =
// 十二、获取 Fragment 父宿主 Fiber
getFragmentParentHostFiber(fragmentFiber);
while (current !== null) {
if (
(current.tag === HostComponent || current.tag === HostRoot) &&
(current === fiberHostParent || current.alternate === fiberHostParent)
) {
return true;
}
current = current.return;
}
return false;
}

十九、 是否 Fiber 优先

export function isFiberPreceding(fiber: Fiber, otherFiber: Fiber): boolean {
const commonAncestor = getLowestCommonAncestor(
fiber,
otherFiber,
// 工具 - 2

getParentForFragmentAncestors,
);
if (commonAncestor === null) {
return false;
}
// 工具 - 1
traverseVisibleHostChildren(
commonAncestor,
true,
isFiberPrecedingCheck,
otherFiber,
fiber,
);
const target = popSearchTarget();
pushSearchTarget(null);
return target !== null;
}

function isFiberPrecedingCheck(
child: Fiber,
target: Fiber,
boundary: Fiber,
): boolean {
if (child === boundary) {
return true;
}
if (child === target) {
pushSearchTarget(child);
return true;
}
return false;
}

二十、是否 Fiber 殿后

export function isFiberFollowing(fiber: Fiber, otherFiber: Fiber): boolean {
const commonAncestor = getLowestCommonAncestor(
fiber,
otherFiber,
// 工具 - 2
getParentForFragmentAncestors,
);
if (commonAncestor === null) {
return false;
}
// 工具 - 1
traverseVisibleHostChildren(
commonAncestor,
true,
isFiberFollowingCheck,
otherFiber,
fiber,
);
const target = popSearchTarget();
pushSearchTarget(null);
pushSearchBoundary(null);
return target !== null;
}

function isFiberFollowingCheck(
child: Fiber,
target: Fiber,
boundary: Fiber,
): boolean {
if (child === boundary) {
pushSearchBoundary(child);
return false;
}
if (child === target) {
// The target is only following if we already found the boundary.
if (popSearchBoundary() !== null) {
pushSearchTarget(child);
}
return true;
}
return false;
}

廿一、获取最低公共祖先

/**
* Return the lowest common ancestor of A and B, or null if they are in
* different trees.
* 返回 A 和 B 的最近公共祖先,如果它们在不同的树中则返回 null。
*/
export function getLowestCommonAncestor(
instA: Fiber,
instB: Fiber,
getParent: (inst: Fiber | null) => Fiber | null,
): Fiber | null {
let nodeA: null | Fiber = instA;
let nodeB: null | Fiber = instB;
let depthA = 0;
for (let tempA: null | Fiber = nodeA; tempA; tempA = getParent(tempA)) {
depthA++;
}
let depthB = 0;
for (let tempB: null | Fiber = nodeB; tempB; tempB = getParent(tempB)) {
depthB++;
}

// If A is deeper, crawl up.
// 如果 A 更深,向上爬。
while (depthA - depthB > 0) {
nodeA = getParent(nodeA);
depthA--;
}

// If B is deeper, crawl up.
// 如果 B 更深,向上爬。
while (depthB - depthA > 0) {
nodeB = getParent(nodeB);
depthB--;
}

// Walk in lockstep until we find a match.
// 一起同步前进,直到找到匹配项。
let depth = depthA;
while (depth--) {
if (nodeA === nodeB || (nodeB !== null && nodeA === nodeB.alternate)) {
return nodeA;
}
nodeA = getParent(nodeA);
nodeB = getParent(nodeB);
}
return null;
}

廿二、工具

1. 遍历可见的宿主子元素

function traverseVisibleHostChildren<A, B, C>(
child: Fiber | null,
searchWithinHosts: boolean,
fn: (Fiber, A, B, C) => boolean,
a: A,
b: B,
c: C,
): boolean {
while (child !== null) {
if (child.tag === HostComponent && fn(child, a, b, c)) {
return true;
} else if (
child.tag === OffscreenComponent &&
child.memoizedState !== null
) {
// Skip hidden subtrees
} else {
if (
(searchWithinHosts || child.tag !== HostComponent) &&
// 工具 - 1
traverseVisibleHostChildren(child.child, searchWithinHosts, fn, a, b, c)
) {
return true;
}
}
child = child.sibling;
}
return false;
}

2.获取碎片祖先的父级

function getParentForFragmentAncestors(inst: Fiber | null): Fiber | null {
if (inst === null) {
return null;
}
do {
inst = inst === null ? null : inst.return;
} while (
inst &&
inst.tag !== HostComponent &&
inst.tag !== HostSingleton &&
inst.tag !== HostRoot
);
if (inst) {
return inst;
}
return null;
}

廿三、常数

1.检索

// 在源码的  485 - 486
// 检索目标
let searchTarget = null;
// 检索边界
let searchBoundary = null;
function pushSearchTarget(target: null | Fiber): void {
searchTarget = target;
}
function popSearchTarget(): null | Fiber {
return searchTarget;
}
function pushSearchBoundary(value: null | Fiber): void {
searchBoundary = value;
}
function popSearchBoundary(): null | Fiber {
return searchBoundary;
}