React以往版本已有Suspense本次改版并没有多大改变。
Suspense
首次挂在时还是会返回Suspense组件,如果遇到了throw(Lazy)的Promise会被捕获到然后一致像父级查找最近的第一个Suspense组件
do {
try {
workLoopSync();
break;
} catch (thrownValue) {
// 捕获错误
handleError(root, thrownValue);
}
} while (true);
function throwException(root, returnFiber, sourceFiber, value, rootRenderLanes) {
// The source fiber did not complete.
sourceFiber.flags |= Incomplete;
// 判断错误是否是一个Promise对象
if (value !== null && typeof value === 'object' && typeof value.then === 'function') {
var wakeable = value;
// 向上查找父级suspense组件
var suspenseBoundary = getNearestSuspenseBoundaryToCapture(returnFiber);
if (suspenseBoundary !== null) {
// code...
//添加ping
attachRetryListener(suspenseBoundary, root, wakeable);
return;
}
// 处理错误, 所以如果没有被包裹就会报错
}
function attachPingListener(root: FiberRoot, wakeable: Wakeable, lanes: Lanes) {
let pingCache = root.pingCache;
// 添加关联的id
let threadIDs;
if (pingCache === null) {
pingCache = root.pingCache = new PossiblyWeakMap();
threadIDs = new Set();
pingCache.set(wakeable, threadIDs);
} else {
threadIDs = pingCache.get(wakeable);
if (threadIDs === undefined) {
threadIDs = new Set();
pingCache.set(wakeable, threadIDs);
}
}
// 只有第一个promise才会被处理
if (!threadIDs.has(lanes)) {
threadIDs.add(lanes);
const ping = pingSuspendedRoot.bind(null, root, wakeable, lanes);
// 也就是 promise().then(func, function)
wakeable.then(ping, ping);
}
}
export function pingSuspendedRoot(
root: FiberRoot,
wakeable: Wakeable,
pingedLanes: Lanes,
) {
const pingCache = root.pingCache;
if (pingCache !== null) {
// 此时promise已经执行完毕,从缓存中删除
pingCache.delete(wakeable);
}
const eventTime = requestEventTime();
markRootPinged(root, pingedLanes, eventTime);
// 同时存在两个ping
if (
workInProgressRoot === root &&
isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)
) {
// 重新设置一次ping
if (
workInProgressRootExitStatus === RootSuspendedWithDelay ||
(workInProgressRootExitStatus === RootSuspended &&
includesOnlyRetries(workInProgressRootRenderLanes) &&
now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)
) {
// 准备一次新的调用环境
prepareFreshStack(root, NoLanes);
} else {
// 合并 pending及已经存在的lane
workInProgressRootPingedLanes = mergeLanes(
workInProgressRootPingedLanes,
pingedLanes,
);
}
}
// 开始调度一次更新
ensureRootIsScheduled(root, eventTime);
schedulePendingInteractions(root, pingedLanes);
}
由于我们的异步还没有执行完毕,也就是Promise的状态没有改变,所以在遇到Sunpense组件时,下面的子fiber节点,被抛出后并不会再继续工作创建fiber节点,而是返回fallback组件。等到promise状态改变以后,从根节点再发起一次更新也就是ensureRootIsScheduled
函数的调用
这个函数是整个应用的第二个入口,第一个是schedulerUpdateOnFiber❤️
throwException添加ping后,当前的unitWork可能是LazyComponent,这里promise被当成错误被抛出,Lazy
是异步组件,此时还没有加载完毕,所以没有必要在继续Lazy及子组件的beginWork. Lazy到Susponse中间的组件已经走过了beginWork, 但是现在要在Lazy开始走complete过层呢, 所以这里走的completeWork,为lazy和向上第一个Susponse组件中间的组件走complete过程,因为这些组件进行了beginWork但是不一定全是异步组件,也可能是HostComponent。
Lazy
lazy是React提供接收异步组件的api,返回的组件是LazyComponent
function mountLazyComponent(_current, workInProgress, elementType, renderLanes) {
if (_current !== null) {
_current.alternate = null;
workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect
workInProgress.flags |= Placement;
}
var props = workInProgress.pendingProps;
var lazyComponent = elementType;
// payload 是防止lazyCompoent身上的
// 是个对象和Context的provader组件蕾丝
var payload = lazyComponent._payload;
var init = lazyComponent._init;
// lazy 内部传入的init函数
// init函数是内部放的
var Component = init(payload); // Store the unwrapped component in the type.
workInProgress.type = Component;
var resolvedTag = workInProgress.tag = resolveLazyComponentTag(Component);
var resolvedProps = resolveDefaultProps(Component, props);
var child;
// code..。
// 这里的处理和其他组件首次挂载时一致,比如FunctionCompoent, ClassCmp
throw new Error("Element type is invalid. Received a promise that resolves to: " + Component + ". " + ("Lazy element type must resolve to a class or function." + hint));
}
var Uninitialized = -1;
var Pending = 0;
var Resolved = 1;
var Rejected = 2;
function lazyInitializer(payload) {
if (payload._status === Uninitialized) {
var ctor = payload._result;
var thenable = ctor(); // 执行(function ctor() => import(http://Component));
thenable.then(function (moduleObject) {
// 此时的paylad和LazyCompoent仍有关联,属于引用关系
// 所以在这里修改以后,cmp一样,等下次进来的时候就是moduleObject
// 非常的巧妙
if (payload._status === Pending || payload._status === Uninitialized) {
var resolved = payload;
resolved._status = Resolved;
resolved._result = moduleObject;
}
}, function (error) {
// errors...
});
if (payload._status === Uninitialized) {
// 未返回的promise
var pending = payload;
pending._status = Pending;
pending._result = thenable;
}
}
if (payload._status === Resolved) {
var moduleObject = payload._result;
// dev import返回的对象,如果是默认到处就是default否则呢就是对象
return moduleObject.default;
} else {
// throw 这个promise,被捕获被suspense处理
// 如果没有被suspense包裹就会报错
throw payload._result;
}
}
// lazy
// lazt函数是React提供的,就是返回一个对象
function lazy(ctor) {
var payload = {
// We use these fields to store the result.
_status: Uninitialized,
_result: ctor
};
var lazyType = {
$$typeof: REACT_LAZY_TYPE,
_payload: payload,
_init: lazyInitializer
};
// dev code...
return lazyType;
}