From a332ba1290797f70d43f25975fc2abc2f0310cbe Mon Sep 17 00:00:00 2001 From: * <8> Date: Wed, 30 Mar 2022 16:00:54 +0800 Subject: [PATCH] Match-id-8baa3f8af8658ae0f6c81042846dfb64857a7df1 --- libs/horizon/src/renderer/ErrorHandler.ts | 8 ++-- libs/horizon/src/renderer/Types.ts | 7 +++ libs/horizon/src/renderer/hooks/HookMain.ts | 2 +- .../src/renderer/render/ClassComponent.ts | 25 +++++------ .../src/renderer/render/FunctionComponent.ts | 10 +---- .../src/renderer/render/SuspenseComponent.ts | 45 ++++++++++--------- libs/horizon/src/renderer/render/index.ts | 6 +-- .../src/renderer/submit/LifeCycleHandler.ts | 6 +-- libs/horizon/src/renderer/vnode/VNode.ts | 39 +++++++++++----- .../src/renderer/vnode/VNodeCreator.ts | 6 +-- libs/horizon/src/renderer/vnode/VNodeUtils.ts | 3 +- 11 files changed, 87 insertions(+), 70 deletions(-) diff --git a/libs/horizon/src/renderer/ErrorHandler.ts b/libs/horizon/src/renderer/ErrorHandler.ts index 837f25c5..8eaed4f8 100644 --- a/libs/horizon/src/renderer/ErrorHandler.ts +++ b/libs/horizon/src/renderer/ErrorHandler.ts @@ -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 { + 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) { diff --git a/libs/horizon/src/renderer/Types.ts b/libs/horizon/src/renderer/Types.ts index f95c4720..6e1572fe 100644 --- a/libs/horizon/src/renderer/Types.ts +++ b/libs/horizon/src/renderer/Types.ts @@ -54,3 +54,10 @@ export interface PromiseType { ): void | PromiseType; } +export interface SuspenseState { + promiseSet: Set> | null; // suspense组件的promise列表 + childStatus: string; + oldChildStatus: string; // 上一次Suspense的Children是否显示 + didCapture: boolean; // suspense是否捕获了异常 + promiseResolved: boolean; // suspense的promise是否resolve +} diff --git a/libs/horizon/src/renderer/hooks/HookMain.ts b/libs/horizon/src/renderer/hooks/HookMain.ts index f4c568ea..1ae324be 100644 --- a/libs/horizon/src/renderer/hooks/HookMain.ts +++ b/libs/horizon/src/renderer/hooks/HookMain.ts @@ -20,7 +20,7 @@ import {useReducerImpl} from './UseReducerHook'; import {HookStage, setHookStage} from './HookStage'; // hook对外入口 -export function exeFunctionHook, Arg>( +export function runFunctionWithHooks, Arg>( funcComp: (props: Props, arg: Arg) => any, props: Props, arg: Arg, diff --git a/libs/horizon/src/renderer/render/ClassComponent.ts b/libs/horizon/src/renderer/render/ClassComponent.ts index 002e343d..228ae62a 100644 --- a/libs/horizon/src/renderer/render/ClassComponent.ts +++ b/libs/horizon/src/renderer/render/ClassComponent.ts @@ -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); } diff --git a/libs/horizon/src/renderer/render/FunctionComponent.ts b/libs/horizon/src/renderer/render/FunctionComponent.ts index 8191293b..563cda52 100644 --- a/libs/horizon/src/renderer/render/FunctionComponent.ts +++ b/libs/horizon/src/renderer/render/FunctionComponent.ts @@ -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, diff --git a/libs/horizon/src/renderer/render/SuspenseComponent.ts b/libs/horizon/src/renderer/render/SuspenseComponent.ts index 0686dc3f..38a84fbc 100644 --- a/libs/horizon/src/renderer/render/SuspenseComponent.ts +++ b/libs/horizon/src/renderer/render/SuspenseComponent.ts @@ -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 | 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): 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) { 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) { // 对于每个promise,添加一个侦听器,以便当它resolve时,重新渲染 export function listenToPromise(suspenseVNode: VNode) { - const promises: Set> | null = suspenseVNode.suspensePromises; + const promises: Set> | null = suspenseVNode.suspenseState.promiseSet; if (promises !== null) { - suspenseVNode.suspensePromises = null; + suspenseVNode.suspenseState.promiseSet = null; // 记录已经监听的 promise let promiseCache = suspenseVNode.realNode; diff --git a/libs/horizon/src/renderer/render/index.ts b/libs/horizon/src/renderer/render/index.ts index 4497627a..2b6f418d 100644 --- a/libs/horizon/src/renderer/render/index.ts +++ b/libs/horizon/src/renderer/render/index.ts @@ -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, -} +}; diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index adb21135..f4aaaddf 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -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); } } diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index 8c98e673..166c8c61 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -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> | null; // 保存hook - suspenseChildStatus = ''; // Suspense的Children是否显示 depContexts: Array> | null; // FunctionComponent和ClassComponent对context的依赖列表 isDepContextChange: boolean; // context是否变更 dirtyNodes: Array | null = null; // 需要改动的节点数组 @@ -55,12 +69,11 @@ export class VNode { oldHooks: Array> | 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 | 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; diff --git a/libs/horizon/src/renderer/vnode/VNodeCreator.ts b/libs/horizon/src/renderer/vnode/VNodeCreator.ts index 9a677f53..932c7fa7 100644 --- a/libs/horizon/src/renderer/vnode/VNodeCreator.ts +++ b/libs/horizon/src/renderer/vnode/VNodeCreator.ts @@ -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; } diff --git a/libs/horizon/src/renderer/vnode/VNodeUtils.ts b/libs/horizon/src/renderer/vnode/VNodeUtils.ts index acda0b39..d064b38c 100644 --- a/libs/horizon/src/renderer/vnode/VNodeUtils.ts +++ b/libs/horizon/src/renderer/vnode/VNodeUtils.ts @@ -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;