Match-id-8baa3f8af8658ae0f6c81042846dfb64857a7df1
This commit is contained in:
parent
bc59541e0c
commit
a332ba1290
|
@ -2,7 +2,7 @@
|
|||
* 异常错误处理
|
||||
*/
|
||||
|
||||
import type {VNode} from './Types';
|
||||
import type { PromiseType, VNode } from './Types';
|
||||
import type {Update} from './UpdateHandler';
|
||||
|
||||
import {ClassComponent, TreeRoot} from './vnode/VNodeTags';
|
||||
|
@ -62,7 +62,9 @@ function createClassErrorUpdate(
|
|||
}
|
||||
return update;
|
||||
}
|
||||
|
||||
function isPromise(error: any): error is PromiseType<any> {
|
||||
return error !== null && typeof error === 'object' && typeof error.then === 'function'
|
||||
}
|
||||
// 处理capture和bubble阶段抛出的错误
|
||||
export function handleRenderThrowError(
|
||||
sourceVNode: VNode,
|
||||
|
@ -74,7 +76,7 @@ export function handleRenderThrowError(
|
|||
sourceVNode.dirtyNodes = null;
|
||||
|
||||
// error是个promise
|
||||
if (error !== null && typeof error === 'object' && typeof error.then === 'function') {
|
||||
if (isPromise(error)) {
|
||||
// 抛出异常的节点,向上寻找,是否有suspense组件
|
||||
const foundSuspense = handleSuspenseChildThrowError(sourceVNode.parent, sourceVNode, error);
|
||||
if (foundSuspense) {
|
||||
|
|
|
@ -54,3 +54,10 @@ export interface PromiseType<R> {
|
|||
): void | PromiseType<U>;
|
||||
}
|
||||
|
||||
export interface SuspenseState {
|
||||
promiseSet: Set<PromiseType<any>> | null; // suspense组件的promise列表
|
||||
childStatus: string;
|
||||
oldChildStatus: string; // 上一次Suspense的Children是否显示
|
||||
didCapture: boolean; // suspense是否捕获了异常
|
||||
promiseResolved: boolean; // suspense的promise是否resolve
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import {useReducerImpl} from './UseReducerHook';
|
|||
import {HookStage, setHookStage} from './HookStage';
|
||||
|
||||
// hook对外入口
|
||||
export function exeFunctionHook<Props extends Record<string, any>, Arg>(
|
||||
export function runFunctionWithHooks<Props extends Record<string, any>, Arg>(
|
||||
funcComp: (props: Props, arg: Arg) => any,
|
||||
props: Props,
|
||||
arg: Arg,
|
||||
|
|
|
@ -33,25 +33,25 @@ export function getCurrentContext(clazz, processing: VNode) {
|
|||
}
|
||||
|
||||
// 挂载实例
|
||||
function mountInstance(clazz, processing: VNode, nextProps: object) {
|
||||
function mountInstance(ctor, processing: VNode, nextProps: object) {
|
||||
if (!processing.isCreated) {
|
||||
processing.isCreated = true;
|
||||
FlagUtils.markAddition(processing);
|
||||
}
|
||||
|
||||
// 构造实例
|
||||
const inst = callConstructor(processing, clazz, nextProps);
|
||||
const inst = callConstructor(processing, ctor, nextProps);
|
||||
|
||||
inst.props = nextProps;
|
||||
inst.state = processing.state;
|
||||
inst.context = getCurrentContext(clazz, processing);
|
||||
inst.context = getCurrentContext(ctor, processing);
|
||||
inst.refs = {};
|
||||
|
||||
processUpdates(processing, inst, nextProps);
|
||||
inst.state = processing.state;
|
||||
|
||||
// 在调用类组建的渲染方法之前调用 并且在初始挂载及后续更新时都会被调用
|
||||
callDerivedStateFromProps(processing, clazz.getDerivedStateFromProps, nextProps);
|
||||
callDerivedStateFromProps(processing, ctor.getDerivedStateFromProps, nextProps);
|
||||
callComponentWillMount(processing, inst, nextProps);
|
||||
|
||||
markComponentDidMount(processing);
|
||||
|
@ -98,13 +98,10 @@ function markLifeCycle(processing: VNode, nextProps: object, shouldUpdate: Boole
|
|||
|
||||
// 用于类组件
|
||||
export function captureRender(processing: VNode): VNode | null {
|
||||
let clazz = processing.type;
|
||||
const ctor = processing.type;
|
||||
let nextProps = processing.props;
|
||||
if (processing.isLazyComponent) {
|
||||
nextProps = mergeDefaultProps(clazz, nextProps);
|
||||
if (processing.promiseResolve) { // 该函数被 lazy 组件使用,未加载的组件需要加载组件的真实内容
|
||||
clazz = clazz._load(clazz._content);
|
||||
}
|
||||
nextProps = mergeDefaultProps(ctor, nextProps);
|
||||
}
|
||||
|
||||
resetDepContexts(processing);
|
||||
|
@ -114,10 +111,10 @@ export function captureRender(processing: VNode): VNode | null {
|
|||
const inst = processing.realNode;
|
||||
if (inst === null) {
|
||||
// 挂载新组件,一定会更新
|
||||
mountInstance(clazz, processing, nextProps);
|
||||
mountInstance(ctor, processing, nextProps);
|
||||
shouldUpdate = true;
|
||||
} else { // 更新
|
||||
const newContext = getCurrentContext(clazz, processing);
|
||||
const newContext = getCurrentContext(ctor, processing);
|
||||
|
||||
// 子节点抛出异常时,如果本class是个捕获异常的处理节点,这时候oldProps是null,所以需要使用props
|
||||
const oldProps = (processing.flags & DidCapture) === DidCapture ? processing.props : processing.oldProps;
|
||||
|
@ -137,13 +134,13 @@ export function captureRender(processing: VNode): VNode | null {
|
|||
|
||||
if (shouldUpdate) {
|
||||
// derivedStateFromProps会修改nextState,因此需要调用
|
||||
callDerivedStateFromProps(processing, clazz.getDerivedStateFromProps, nextProps);
|
||||
callDerivedStateFromProps(processing, ctor.getDerivedStateFromProps, nextProps);
|
||||
if (!processing.isForceUpdate) {
|
||||
// 业务可以通过 shouldComponentUpdate 函数进行优化阻止更新
|
||||
shouldUpdate = callShouldComponentUpdate(processing, oldProps, nextProps, processing.state, newContext);
|
||||
}
|
||||
if (shouldUpdate) {
|
||||
callUpdateLifeCycle(processing, nextProps, clazz);
|
||||
callUpdateLifeCycle(processing, nextProps, ctor);
|
||||
}
|
||||
inst.state = processing.state;
|
||||
inst.context = newContext;
|
||||
|
@ -162,7 +159,7 @@ export function captureRender(processing: VNode): VNode | null {
|
|||
|
||||
// 不复用
|
||||
if (shouldUpdate) {
|
||||
return createChildren(clazz, processing);
|
||||
return createChildren(ctor, processing);
|
||||
} else {
|
||||
return onlyUpdateChildVNodes(processing);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import type { VNode } from '../Types';
|
|||
|
||||
import { mergeDefaultProps } from './LazyComponent';
|
||||
import { resetDepContexts } from '../components/context/Context';
|
||||
import { exeFunctionHook } from '../hooks/HookMain';
|
||||
import { runFunctionWithHooks } from '../hooks/HookMain';
|
||||
import { ForwardRef } from '../vnode/VNodeTags';
|
||||
import { FlagUtils, Update } from '../vnode/VNodeFlags';
|
||||
import { getContextChangeCtx } from '../ContextSaver';
|
||||
|
@ -26,12 +26,6 @@ function checkIfCanReuseChildren(processing: VNode, shouldUpdate?: boolean) {
|
|||
// 如果props或者context改变了
|
||||
if (oldProps !== newProps || getContextChangeCtx() || processing.isDepContextChange) {
|
||||
isCanReuse = false;
|
||||
} else {
|
||||
if (shouldUpdate && processing.suspenseChildThrow) {
|
||||
// 使用完后恢复
|
||||
processing.suspenseChildThrow = false;
|
||||
isCanReuse = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
isCanReuse = false;
|
||||
|
@ -68,7 +62,7 @@ export function captureFunctionComponent(
|
|||
// 在执行exeFunctionHook前先设置stateChange为false
|
||||
setStateChange(false);
|
||||
|
||||
const newElements = exeFunctionHook(
|
||||
const newElements = runFunctionWithHooks(
|
||||
processing.tag === ForwardRef ? funcComp.render : funcComp,
|
||||
nextProps,
|
||||
processing.tag === ForwardRef ? processing.ref : undefined,
|
||||
|
|
|
@ -4,6 +4,7 @@ import {FlagUtils, Interrupted} from '../vnode/VNodeFlags';
|
|||
import {onlyUpdateChildVNodes, updateVNode, createFragmentVNode} from '../vnode/VNodeCreator';
|
||||
import {
|
||||
ClassComponent,
|
||||
ForwardRef,
|
||||
FunctionComponent,
|
||||
IncompleteClassComponent,
|
||||
SuspenseComponent,
|
||||
|
@ -47,7 +48,7 @@ function createFallback(processing: VNode, fallbackChildren) {
|
|||
fallbackFragment.eIndex = 1;
|
||||
fallbackFragment.cIndex = 1;
|
||||
markVNodePath(fallbackFragment);
|
||||
processing.suspenseChildStatus = SuspenseChildStatus.ShowFallback;
|
||||
processing.suspenseState.childStatus = SuspenseChildStatus.ShowFallback;
|
||||
|
||||
return fallbackFragment;
|
||||
}
|
||||
|
@ -71,7 +72,7 @@ function createSuspenseChildren(processing: VNode, newChildren) {
|
|||
processing.dirtyNodes = [oldFallbackFragment];
|
||||
}
|
||||
// SuspenseComponent 中使用
|
||||
processing.suspenseChildStatus = SuspenseChildStatus.ShowChild;
|
||||
processing.suspenseState.childStatus = SuspenseChildStatus.ShowChild;
|
||||
} else {
|
||||
childFragment = createFragmentVNode(null, newChildren);
|
||||
}
|
||||
|
@ -80,7 +81,7 @@ function createSuspenseChildren(processing: VNode, newChildren) {
|
|||
childFragment.cIndex = 0;
|
||||
markVNodePath(childFragment);
|
||||
processing.child = childFragment;
|
||||
processing.promiseResolve = false;
|
||||
processing.suspenseState.promiseResolved = false;
|
||||
return processing.child;
|
||||
}
|
||||
|
||||
|
@ -88,10 +89,10 @@ export function captureSuspenseComponent(processing: VNode) {
|
|||
const nextProps = processing.props;
|
||||
|
||||
// suspense被捕获后需要展示fallback
|
||||
const showFallback = processing.suspenseDidCapture;
|
||||
const showFallback = processing.suspenseState.didCapture;
|
||||
|
||||
if (showFallback) {
|
||||
processing.suspenseDidCapture = false;
|
||||
processing.suspenseState.didCapture = false;
|
||||
const nextFallbackChildren = nextProps.fallback;
|
||||
return createFallback(processing, nextFallbackChildren);
|
||||
} else {
|
||||
|
@ -104,12 +105,12 @@ function updateFallback(processing: VNode): Array<VNode> | VNode | null {
|
|||
const childFragment: VNode | null= processing.child;
|
||||
|
||||
if (childFragment?.childShouldUpdate) {
|
||||
if (processing.promiseResolve) {
|
||||
if (processing.suspenseState.promiseResolved) {
|
||||
// promise已完成,展示promise返回的新节点
|
||||
return captureSuspenseComponent(processing);
|
||||
} else {
|
||||
// promise未完成,继续显示fallback,不需要继续刷新子节点
|
||||
const fallbackFragment: VNode = processing.child.next;
|
||||
const fallbackFragment: VNode = processing.child!.next!;
|
||||
childFragment.childShouldUpdate = false;
|
||||
fallbackFragment.childShouldUpdate = false;
|
||||
return null;
|
||||
|
@ -133,7 +134,7 @@ export function captureRender(processing: VNode, shouldUpdate: boolean): Array<V
|
|||
!getContextChangeCtx() &&
|
||||
!shouldUpdate
|
||||
) {
|
||||
if (processing.suspenseChildStatus === SuspenseChildStatus.ShowFallback) {
|
||||
if (processing.suspenseState.childStatus === SuspenseChildStatus.ShowFallback) {
|
||||
// 当显示fallback时,suspense的子组件要更新
|
||||
return updateFallback(processing);
|
||||
}
|
||||
|
@ -144,8 +145,9 @@ export function captureRender(processing: VNode, shouldUpdate: boolean): Array<V
|
|||
}
|
||||
|
||||
export function bubbleRender(processing: VNode) {
|
||||
if (processing.suspenseChildStatus === SuspenseChildStatus.ShowFallback
|
||||
|| (!processing.isCreated && processing.oldSuspenseChildStatus === SuspenseChildStatus.ShowFallback)
|
||||
const { childStatus, oldChildStatus } = processing.suspenseState;
|
||||
if (childStatus === SuspenseChildStatus.ShowFallback
|
||||
|| (!processing.isCreated && oldChildStatus === SuspenseChildStatus.ShowFallback)
|
||||
) {
|
||||
FlagUtils.markUpdate(processing);
|
||||
}
|
||||
|
@ -154,22 +156,21 @@ export function bubbleRender(processing: VNode) {
|
|||
}
|
||||
|
||||
function canCapturePromise(vNode: VNode | null): boolean {
|
||||
return vNode?.suspenseChildStatus !== SuspenseChildStatus.ShowFallback && vNode?.props.fallback !== undefined;
|
||||
return vNode?.suspenseState.childStatus !== SuspenseChildStatus.ShowFallback && vNode?.props.fallback !== undefined;
|
||||
}
|
||||
|
||||
// 处理Suspense子组件抛出的promise
|
||||
export function handleSuspenseChildThrowError(parent: VNode, processing: VNode, error: any): boolean {
|
||||
export function handleSuspenseChildThrowError(parent: VNode, processing: VNode, promise: PromiseType<any>): boolean {
|
||||
let vNode = parent;
|
||||
|
||||
// 向上找到最近的不在fallback状态的Suspense,并触发重新渲染
|
||||
do {
|
||||
if (vNode.tag === SuspenseComponent && canCapturePromise(vNode)) {
|
||||
if (vNode.suspensePromises === null) {
|
||||
vNode.suspensePromises = new Set();
|
||||
if (vNode.suspenseState.promiseSet === null) {
|
||||
vNode.suspenseState.promiseSet = new Set();
|
||||
}
|
||||
vNode.suspensePromises.add(error);
|
||||
vNode.suspenseState.promiseSet.add(promise);
|
||||
|
||||
processing.suspenseChildThrow = true;
|
||||
|
||||
// 移除生命周期flag 和 中断flag
|
||||
FlagUtils.removeLifecycleEffectFlags(processing);
|
||||
|
@ -186,13 +187,13 @@ export function handleSuspenseChildThrowError(parent: VNode, processing: VNode,
|
|||
}
|
||||
}
|
||||
|
||||
if(processing.tag === FunctionComponent) {
|
||||
processing.isSuspended = true;
|
||||
if (processing.tag === FunctionComponent || processing.tag === ForwardRef) {
|
||||
processing.isSuspended= true;
|
||||
}
|
||||
// 应该抛出promise未完成更新,标志待更新
|
||||
processing.shouldUpdate = true;
|
||||
|
||||
vNode.suspenseDidCapture = true;
|
||||
vNode.suspenseState.didCapture = true;
|
||||
launchUpdateFromVNode(vNode);
|
||||
|
||||
return true;
|
||||
|
@ -211,7 +212,7 @@ function resolvePromise(suspenseVNode: VNode, promise: PromiseType<any>) {
|
|||
if (promiseCache !== null) {
|
||||
promiseCache.delete(promise);
|
||||
}
|
||||
suspenseVNode.promiseResolve = true;
|
||||
suspenseVNode.suspenseState.promiseResolved = true;
|
||||
const root = updateShouldUpdateOfTree(suspenseVNode);
|
||||
if (root !== null) {
|
||||
tryRenderFromRoot(root);
|
||||
|
@ -220,9 +221,9 @@ function resolvePromise(suspenseVNode: VNode, promise: PromiseType<any>) {
|
|||
|
||||
// 对于每个promise,添加一个侦听器,以便当它resolve时,重新渲染
|
||||
export function listenToPromise(suspenseVNode: VNode) {
|
||||
const promises: Set<PromiseType<any>> | null = suspenseVNode.suspensePromises;
|
||||
const promises: Set<PromiseType<any>> | null = suspenseVNode.suspenseState.promiseSet;
|
||||
if (promises !== null) {
|
||||
suspenseVNode.suspensePromises = null;
|
||||
suspenseVNode.suspenseState.promiseSet = null;
|
||||
|
||||
// 记录已经监听的 promise
|
||||
let promiseCache = suspenseVNode.realNode;
|
||||
|
|
|
@ -32,8 +32,8 @@ import {
|
|||
} from '../vnode/VNodeTags';
|
||||
|
||||
export {
|
||||
BaseComponentRender
|
||||
}
|
||||
BaseComponentRender,
|
||||
};
|
||||
|
||||
export default {
|
||||
[ClassComponent]: ClassComponentRender,
|
||||
|
@ -50,4 +50,4 @@ export default {
|
|||
[LazyComponent]: LazyComponentRender,
|
||||
[MemoComponent]: MemoComponentRender,
|
||||
[SuspenseComponent]: SuspenseComponentRender,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -399,9 +399,9 @@ function submitUpdate(vNode: VNode): void {
|
|||
}
|
||||
|
||||
function submitSuspenseComponent(vNode: VNode) {
|
||||
const suspenseChildStatus = vNode.suspenseChildStatus;
|
||||
if (suspenseChildStatus !== SuspenseChildStatus.Init) {
|
||||
hideOrUnhideAllChildren(vNode.child, suspenseChildStatus === SuspenseChildStatus.ShowFallback);
|
||||
const { childStatus } = vNode.suspenseState;
|
||||
if (childStatus !== SuspenseChildStatus.Init) {
|
||||
hideOrUnhideAllChildren(vNode.child, childStatus === SuspenseChildStatus.ShowFallback);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,25 @@
|
|||
/**
|
||||
* 虚拟DOM结构体
|
||||
*/
|
||||
import { TreeRoot, FunctionComponent, ClassComponent, DomPortal, DomText, ContextConsumer, ForwardRef, SuspenseComponent, LazyComponent, DomComponent, Fragment, ContextProvider, Profiler, MemoComponent, IncompleteClassComponent } from './VNodeTags';
|
||||
import {
|
||||
TreeRoot,
|
||||
FunctionComponent,
|
||||
ClassComponent,
|
||||
DomPortal,
|
||||
DomText,
|
||||
ContextConsumer,
|
||||
ForwardRef,
|
||||
SuspenseComponent,
|
||||
LazyComponent,
|
||||
DomComponent,
|
||||
Fragment,
|
||||
ContextProvider,
|
||||
Profiler,
|
||||
MemoComponent,
|
||||
IncompleteClassComponent,
|
||||
} from './VNodeTags';
|
||||
import type { VNodeTag } from './VNodeTags';
|
||||
import type { RefType, ContextType } from '../Types';
|
||||
import type { RefType, ContextType, SuspenseState } from '../Types';
|
||||
import type { Hook } from '../hooks/HookType';
|
||||
import { InitFlag } from './VNodeFlags';
|
||||
|
||||
|
@ -24,7 +40,6 @@ export class VNode {
|
|||
ref: RefType | ((handle: any) => void) | null = null; // 包裹一个函数,submit阶段使用,比如将外部useRef生成的对象赋值到ref上
|
||||
oldProps: any = null;
|
||||
|
||||
suspensePromises: any; // suspense组件的promise列表
|
||||
changeList: any; // DOM的变更列表
|
||||
effectList: any[] | null; // useEffect 的更新数组
|
||||
updates: any[] | null; // TreeRoot和ClassComponent使用的更新数组
|
||||
|
@ -33,7 +48,6 @@ export class VNode {
|
|||
isSuspended = false; // 是否被suspense打断更新
|
||||
state: any; // ClassComponent和TreeRoot的状态
|
||||
hooks: Array<Hook<any, any>> | null; // 保存hook
|
||||
suspenseChildStatus = ''; // Suspense的Children是否显示
|
||||
depContexts: Array<ContextType<any>> | null; // FunctionComponent和ClassComponent对context的依赖列表
|
||||
isDepContextChange: boolean; // context是否变更
|
||||
dirtyNodes: Array<VNode> | null = null; // 需要改动的节点数组
|
||||
|
@ -55,12 +69,11 @@ export class VNode {
|
|||
oldHooks: Array<Hook<any, any>> | null; // 保存上一次执行的hook
|
||||
oldState: any;
|
||||
oldRef: RefType | ((handle: any) => void) | null = null;
|
||||
suspenseChildThrow: boolean;
|
||||
oldSuspenseChildStatus: string; // 上一次Suspense的Children是否显示
|
||||
oldChild: VNode | null = null;
|
||||
suspenseDidCapture: boolean; // suspense是否捕获了异常
|
||||
promiseResolve: boolean; // suspense的promise是否resolve
|
||||
|
||||
suspenseState: SuspenseState;
|
||||
|
||||
path = ''; // 保存从根到本节点的路径
|
||||
toUpdateNodes: Set<VNode> | null; // 保存要更新的节点
|
||||
|
||||
|
@ -116,11 +129,13 @@ export class VNode {
|
|||
break;
|
||||
case SuspenseComponent:
|
||||
this.realNode = null;
|
||||
this.suspensePromises = null;
|
||||
this.suspenseChildThrow = false;
|
||||
this.suspenseDidCapture = false;
|
||||
this.promiseResolve = false;
|
||||
this.oldSuspenseChildStatus = '';
|
||||
this.suspenseState = {
|
||||
promiseSet: null,
|
||||
didCapture: false,
|
||||
promiseResolved: false,
|
||||
oldChildStatus: '',
|
||||
childStatus: ''
|
||||
};
|
||||
break;
|
||||
case ContextProvider:
|
||||
this.contexts = null;
|
||||
|
|
|
@ -38,9 +38,9 @@ const typeMap = {
|
|||
[TYPE_LAZY]: LazyComponent,
|
||||
};
|
||||
|
||||
const newVirtualNode = function(tag: VNodeTag, key?: null | string, vNodeProps?: any, realNode?: any): VNode {
|
||||
function newVirtualNode (tag: VNodeTag, key?: null | string, vNodeProps?: any, realNode?: any): VNode {
|
||||
return new VNode(tag, vNodeProps, key, realNode);
|
||||
};
|
||||
}
|
||||
|
||||
function isClassComponent(comp: Function) {
|
||||
// 如果使用 getPrototypeOf 方法获取构造函数,不能兼容业务组组件继承组件的使用方式,会误认为是函数组件
|
||||
|
@ -66,7 +66,7 @@ export function updateVNode(vNode: VNode, vNodeProps?: any): VNode {
|
|||
}
|
||||
|
||||
if (vNode.tag === SuspenseComponent) {
|
||||
vNode.oldSuspenseChildStatus = vNode.suspenseChildStatus;
|
||||
vNode.suspenseState.oldChildStatus = vNode.suspenseState.childStatus;
|
||||
vNode.oldChild = vNode.child;
|
||||
}
|
||||
|
||||
|
|
|
@ -82,7 +82,8 @@ export function clearVNode(vNode: VNode) {
|
|||
vNode.hooks = null;
|
||||
vNode.props = null;
|
||||
vNode.parent = null;
|
||||
vNode.suspensePromises = null;
|
||||
// mark
|
||||
vNode.suspenseState = null;
|
||||
vNode.changeList = null;
|
||||
vNode.effectList = null;
|
||||
vNode.updates = null;
|
||||
|
|
Loading…
Reference in New Issue