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...//添加pingattachRetryListener(suspenseBoundary, root, wakeable);return;}// 处理错误, 所以如果没有被包裹就会报错}function attachPingListener(root: FiberRoot, wakeable: Wakeable, lanes: Lanes) {let pingCache = root.pingCache;// 添加关联的idlet 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);// 同时存在两个pingif (workInProgressRoot === root &&isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)) {// 重新设置一次pingif (workInProgressRootExitStatus === RootSuspendedWithDelay ||(workInProgressRootExitStatus === RootSuspended &&includesOnlyRetries(workInProgressRootRenderLanes) &&now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)) {// 准备一次新的调用环境prepareFreshStack(root, NoLanes);} else {// 合并 pending及已经存在的laneworkInProgressRootPingedLanes = 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;
}
