跳到主要内容

React Fiber 热重载

一、作用

二、设置刷新处理程序

export const setRefreshHandler = (handler: RefreshHandler | null): void => {
if (__DEV__) {
resolveFamily = handler;
}
};

三、为热重载解析函数

export function resolveFunctionForHotReloading(type: any): any {
if (__DEV__) {
if (resolveFamily === null) {
// Hot reloading is disabled.
// 热更新已禁用。
return type;
}
const family = resolveFamily(type);
if (family === undefined) {
return type;
}
// Use the latest known implementation.
// 使用已知的最新实现。
return family.current;
} else {
return type;
}
}

四、 为热重载解析类

export function resolveClassForHotReloading(type: any): any {
// No implementation differences.
// 没有实现差异。
return resolveFunctionForHotReloading(type);
}

五、为热重载解析前向引用

export function resolveForwardRefForHotReloading(type: any): any {
if (__DEV__) {
if (resolveFamily === null) {
// Hot reloading is disabled.
// 热更新已禁用。
return type;
}
const family = resolveFamily(type);
if (family === undefined) {
// Check if we're dealing with a real forwardRef. Don't want to crash early.
// 检查我们是否在处理一个真正的 forwardRef。不要过早崩溃。
if (
type !== null &&
type !== undefined &&
typeof type.render === 'function'
) {
// ForwardRef is special because its resolved .type is an object,
// but it's possible that we only have its inner render function in the map.
// If that inner render function is different, we'll build a new forwardRef type.
//
// ForwardRef 很特别,因为它解析后的 .type 是一个对象,
// 但有可能我们在映射中只拥有它的内部渲染函数。
// 如果那个内部渲染函数不同,我们将构建一个新的 forwardRef 类型。
const currentRender = resolveFunctionForHotReloading(type.render);
if (type.render !== currentRender) {
const syntheticType = {
$$typeof: REACT_FORWARD_REF_TYPE,
render: currentRender,
};
if (type.displayName !== undefined) {
(syntheticType as any).displayName = type.displayName;
}
return syntheticType;
}
}
return type;
}
// Use the latest known implementation.
// 使用已知的最新实现。
return family.current;
} else {
return type;
}
}

六、 是否兼容热重载的组件

export function isCompatibleFamilyForHotReloading(
fiber: Fiber,
element: ReactElement,
): boolean {
if (__DEV__) {
if (resolveFamily === null) {
// Hot reloading is disabled.
// 热更新已禁用。
return false;
}

const prevType = fiber.elementType;
const nextType = element.type;

// If we got here, we know types aren't === equal.
// 如果我们到达这里,我们就知道类型不完全相等。
let needsCompareFamilies = false;

const $$typeofNextType =
typeof nextType === 'object' && nextType !== null
? nextType.$$typeof
: null;

switch (fiber.tag) {
case ClassComponent: {
if (typeof nextType === 'function') {
needsCompareFamilies = true;
}
break;
}
case FunctionComponent: {
if (typeof nextType === 'function') {
needsCompareFamilies = true;
} else if ($$typeofNextType === REACT_LAZY_TYPE) {
// We don't know the inner type yet.
// We're going to assume that the lazy inner type is stable,
// and so it is sufficient to avoid reconciling it away.
// We're not going to unwrap or actually use the new lazy type.
//
// 我们还不知道内部类型。
// 我们将假设懒惰的内部类型是稳定的,
// 因此只需避免将其调和掉即可。
// 我们不会解包或实际使用新的懒惰类型。
needsCompareFamilies = true;
}
break;
}
case ForwardRef: {
if ($$typeofNextType === REACT_FORWARD_REF_TYPE) {
needsCompareFamilies = true;
} else if ($$typeofNextType === REACT_LAZY_TYPE) {
needsCompareFamilies = true;
}
break;
}
case MemoComponent:
case SimpleMemoComponent: {
if ($$typeofNextType === REACT_MEMO_TYPE) {
// TODO: if it was but can no longer be simple,
// we shouldn't set this.
//
// 待办:如果它曾经可以,但现在不再简洁,
// 我们不应该设置它。
needsCompareFamilies = true;
} else if ($$typeofNextType === REACT_LAZY_TYPE) {
needsCompareFamilies = true;
}
break;
}
default:
return false;
}

// Check if both types have a family and it's the same one.
// 检查两种类型是否都有家庭,并且是否相同。
if (needsCompareFamilies) {
// Note: memo() and forwardRef() we'll compare outer rather than inner type.
// This means both of them need to be registered to preserve state.
// If we unwrapped and compared the inner types for wrappers instead,
// then we would risk falsely saying two separate memo(Foo)
// calls are equivalent because they wrap the same Foo function.
//
// 注意:我们将比较 memo() 和 forwardRef() 的外部类型,而不是内部类型。
// 这意味着两者都需要注册以保留状态。
// 如果我们解包并比较包装器的内部类型,
// 那么就有可能错误地认为两个独立的 memo(Foo) 调用是相同的,
// 因为它们包装的是相同的 Foo 函数。
const prevFamily = resolveFamily(prevType);
if (prevFamily !== undefined && prevFamily === resolveFamily(nextType)) {
return true;
}
}
return false;
} else {
return false;
}
}

七、为热加载标记失败的错误边界

export function markFailedErrorBoundaryForHotReloading(fiber: Fiber) {
if (__DEV__) {
if (resolveFamily === null) {
// Hot reloading is disabled.
// 热更新已禁用。
return;
}
if (typeof WeakSet !== 'function') {
return;
}
if (failedBoundaries === null) {
failedBoundaries = new WeakSet();
}
failedBoundaries.add(fiber);
}
}

八、 安排刷新

信息
export const scheduleRefresh: ScheduleRefresh = (
root: FiberRoot,
update: RefreshUpdate,
): void => {
if (__DEV__) {
if (resolveFamily === null) {
// Hot reloading is disabled.
// 热更新已禁用。
return;
}
const { staleFamilies, updatedFamilies } = update;
flushPendingEffects();
scheduleFibersWithFamiliesRecursively(
root.current,
updatedFamilies,
staleFamilies,
);
flushSyncWork();
}
};

九、计划根

备注
export const scheduleRoot: ScheduleRoot = (
root: FiberRoot,
element: ReactNodeList,
): void => {
if (__DEV__) {
if (root.context !== emptyContextObject) {
// Super edge case: root has a legacy _renderSubtree context
// but we don't know the parentComponent so we can't pass it.
// Just ignore. We'll delete this with _renderSubtree code path later.
//
// 超级边缘情况:根节点有一个 legacy _renderSubtree 上下文。但我们不知道
// parentComponent,所以无法传递它。直接忽略。我们稍后会删除这个
// _renderSubtree 代码路径。
return;
}
updateContainerSync(element, root, null, null);
flushSyncWork();
}
};

十、变量

1. 刷新处理程序

// 解决家庭纠纷 (刷新处理程序)
let resolveFamily: RefreshHandler | null = null;

2. 失败的边缘

// 失败的边界
let failedBoundaries: WeakSet<Fiber> | null = null;

十一、工具

1. 递归安排 fiber 与家庭

备注
function scheduleFibersWithFamiliesRecursively(
fiber: Fiber,
updatedFamilies: Set<Family>,
staleFamilies: Set<Family>,
): void {
if (__DEV__) {
do {
const { alternate, child, sibling, tag, type } = fiber;

let candidateType = null;
switch (tag) {
case FunctionComponent:
case SimpleMemoComponent:
case ClassComponent:
candidateType = type;
break;
case ForwardRef:
candidateType = type.render;
break;
default:
break;
}

if (resolveFamily === null) {
throw new Error('Expected resolveFamily to be set during hot reload.');
}

let needsRender = false;
let needsRemount = false;
if (candidateType !== null) {
const family = resolveFamily(candidateType);
if (family !== undefined) {
if (staleFamilies.has(family)) {
needsRemount = true;
} else if (updatedFamilies.has(family)) {
if (tag === ClassComponent) {
needsRemount = true;
} else {
needsRender = true;
}
}
}
}
if (failedBoundaries !== null) {
if (
failedBoundaries.has(fiber) ||
(alternate !== null && failedBoundaries.has(alternate))
) {
needsRemount = true;
}
}

if (needsRemount) {
fiber._debugNeedsRemount = true;
}
if (needsRemount || needsRender) {
const root = enqueueConcurrentRenderForLane(fiber, SyncLane);
if (root !== null) {
scheduleUpdateOnFiber(root, fiber, SyncLane);
}
}
if (child !== null && !needsRemount) {
scheduleFibersWithFamiliesRecursively(
child,
updatedFamilies,
staleFamilies,
);
}

if (sibling === null) {
break;
}
fiber = sibling;
} while (true);
}
}