From 73a652653f3d14c201cc0a2ed7be85c3a53cc379 Mon Sep 17 00:00:00 2001 From: * <8> Date: Wed, 8 Jun 2022 14:37:42 +0800 Subject: [PATCH] Match-id-41cddd29db036a66ad0ef00c0a72ee4c6ef9f29c --- libs/horizon/index.ts | 2 +- libs/horizon/src/renderer/GlobalVar.ts | 9 ++++++++ libs/horizon/src/renderer/TreeBuilder.ts | 21 +++++++++---------- libs/horizon/src/renderer/hooks/BaseHook.ts | 14 +++---------- .../src/renderer/hooks/HookExternal.ts | 2 +- libs/horizon/src/renderer/hooks/HookMain.ts | 4 ---- .../src/renderer/hooks/UseEffectHook.ts | 3 ++- .../src/renderer/hooks/UseReducerHook.ts | 2 +- .../src/renderer/render/BaseComponent.ts | 8 ++++++- .../src/renderer/render/ClassComponent.ts | 4 +++- .../src/renderer/render/FunctionComponent.ts | 2 +- .../src/renderer/submit/LifeCycleHandler.ts | 6 ++++++ libs/horizon/src/renderer/vnode/VNode.ts | 11 +++++++--- 13 files changed, 52 insertions(+), 36 deletions(-) diff --git a/libs/horizon/index.ts b/libs/horizon/index.ts index 71d104a9..320ac08f 100644 --- a/libs/horizon/index.ts +++ b/libs/horizon/index.ts @@ -30,7 +30,7 @@ import { import { launchUpdateFromVNode as _launchUpdateFromVNode, asyncUpdates } from './src/renderer/TreeBuilder'; import { callRenderQueueImmediate } from './src/renderer/taskExecutor/RenderQueue'; import { runAsyncEffects } from './src/renderer/submit/HookEffectHandler'; -import { getProcessingVNode as _getProcessingVNode } from './src/renderer/hooks/BaseHook'; +import { getProcessingVNode as _getProcessingVNode } from './src/renderer/GlobalVar'; // act用于测试,作用是:如果fun触发了刷新(包含了异步刷新),可以保证在act后面的代码是在刷新完成后才执行。 const act = fun => { diff --git a/libs/horizon/src/renderer/GlobalVar.ts b/libs/horizon/src/renderer/GlobalVar.ts index 44911e69..71008d95 100644 --- a/libs/horizon/src/renderer/GlobalVar.ts +++ b/libs/horizon/src/renderer/GlobalVar.ts @@ -9,6 +9,15 @@ export function setProcessingClassVNode(vNode: VNode | null) { processingClassVNode = vNode; } +// capture阶段正在处理的VNode +let processingVNode: VNode | null = null; +export function getProcessingVNode() { + return processingVNode; +} +export function setProcessingVNode(vNode: VNode | null) { + processingVNode = vNode; +} + // 计算出来的刷新节点,不一定是根节点 let startVNode: VNode | null = null; export function getStartVNode(): VNode | null { diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index 923b4275..12626215 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -242,26 +242,25 @@ function buildVNodeTree(treeRoot: VNode) { startVNode.props = startVNode.devProps; startVNode.devProps = undefined; } + while (processing !== null) { try { - while (processing !== null) { - // 捕获创建 vNodes - const next = captureVNode(processing); + // 捕获创建 vNodes + const next = captureVNode(processing); - if (next === null) { - // 如果没有子节点,那么就完成当前节点,开始冒泡 - bubbleVNode(processing); - } else { - processing = next; - } + if (next === null) { + // 如果没有子节点,那么就完成当前节点,开始冒泡 + bubbleVNode(processing); + } else { + processing = next; } - - setProcessingClassVNode(null); } catch (thrownValue) { handleError(treeRoot, thrownValue); } } + setProcessingClassVNode(null); + setExecuteMode(preMode); } diff --git a/libs/horizon/src/renderer/hooks/BaseHook.ts b/libs/horizon/src/renderer/hooks/BaseHook.ts index 54e180c8..52a00695 100644 --- a/libs/horizon/src/renderer/hooks/BaseHook.ts +++ b/libs/horizon/src/renderer/hooks/BaseHook.ts @@ -1,7 +1,5 @@ -import type { VNode } from '../Types'; import type { Hook } from './HookType'; - -let processingVNode: VNode | null = null; +import {getProcessingVNode} from '../GlobalVar'; // lastTimeHook是上一次执行func时产生的hooks中,与currentHook对应的hook let lastTimeHook: Hook | null = null; @@ -9,14 +7,6 @@ let lastTimeHook: Hook | null = null; // 当前hook函数对应的hook对象 let currentHook: Hook | null = null; -export function getProcessingVNode() { - return processingVNode; -} - -export function setProcessingVNode(vNode: VNode | null) { - processingVNode = vNode; -} - export function getLastTimeHook() { return lastTimeHook; } @@ -37,6 +27,7 @@ export function throwNotInFuncError() { // 新建一个hook,并放到vNode.hooks中 export function createHook(state: any = null): Hook { + const processingVNode = getProcessingVNode(); const newHook: Hook = { state: state, hIndex: processingVNode.hooks.length, @@ -56,6 +47,7 @@ export function getNextHook(hook: Hook, hooks: Array>) // processing中的hook和上一次执行中的hook,需要同时往前走, // 原因:1.比对hook的数量有没有变化(非必要);2.从上一次执行中的hook获取removeEffect export function getCurrentHook(): Hook { + const processingVNode = getProcessingVNode(); currentHook = currentHook !== null ? getNextHook(currentHook, processingVNode.hooks) : (processingVNode.hooks[0] || null); diff --git a/libs/horizon/src/renderer/hooks/HookExternal.ts b/libs/horizon/src/renderer/hooks/HookExternal.ts index 307a80cb..ce67040c 100644 --- a/libs/horizon/src/renderer/hooks/HookExternal.ts +++ b/libs/horizon/src/renderer/hooks/HookExternal.ts @@ -8,7 +8,7 @@ import {useImperativeHandleImpl} from './UseImperativeHook'; import {useReducerImpl} from './UseReducerHook'; import {useStateImpl} from './UseStateHook'; import {getNewContext} from '../components/context/Context'; -import {getProcessingVNode} from './BaseHook'; +import {getProcessingVNode} from '../GlobalVar'; import {Ref, Trigger} from './HookType'; type BasicStateAction = ((S) => S) | S; diff --git a/libs/horizon/src/renderer/hooks/HookMain.ts b/libs/horizon/src/renderer/hooks/HookMain.ts index 30cf0e11..d28b3feb 100644 --- a/libs/horizon/src/renderer/hooks/HookMain.ts +++ b/libs/horizon/src/renderer/hooks/HookMain.ts @@ -3,7 +3,6 @@ import type {VNode} from '../Types'; import { getLastTimeHook, setLastTimeHook, - setProcessingVNode, setCurrentHook, getNextHook } from './BaseHook'; import {HookStage, setHookStage} from './HookStage'; @@ -18,8 +17,6 @@ export function runFunctionWithHooks, Arg>( // 重置全局变量 resetGlobalVariable(); - setProcessingVNode(processing); - processing.oldHooks = processing.hooks; processing.hooks = []; processing.effectList = []; @@ -52,7 +49,6 @@ export function runFunctionWithHooks, Arg>( function resetGlobalVariable() { setHookStage(null); - setProcessingVNode(null); setLastTimeHook(null); setCurrentHook(null); } diff --git a/libs/horizon/src/renderer/hooks/UseEffectHook.ts b/libs/horizon/src/renderer/hooks/UseEffectHook.ts index d3418782..f2ac1076 100644 --- a/libs/horizon/src/renderer/hooks/UseEffectHook.ts +++ b/libs/horizon/src/renderer/hooks/UseEffectHook.ts @@ -2,13 +2,14 @@ import { createHook, getCurrentHook, getLastTimeHook, - getProcessingVNode, throwNotInFuncError + throwNotInFuncError } from './BaseHook'; import {FlagUtils} from '../vnode/VNodeFlags'; import {EffectConstant} from './EffectConstant'; import type {Effect, EffectList} from './HookType'; import {getHookStage, HookStage} from './HookStage'; import {isArrayEqual} from '../utils/compare'; +import {getProcessingVNode} from '../GlobalVar'; export function useEffectImpl(effectFunc: () => (() => void) | void, deps?: Array | null,): void { // 异步触发的effect diff --git a/libs/horizon/src/renderer/hooks/UseReducerHook.ts b/libs/horizon/src/renderer/hooks/UseReducerHook.ts index 52399713..c40da904 100644 --- a/libs/horizon/src/renderer/hooks/UseReducerHook.ts +++ b/libs/horizon/src/renderer/hooks/UseReducerHook.ts @@ -2,7 +2,6 @@ import type { Hook, Reducer, Trigger, Update } from './HookType'; import { createHook, getCurrentHook, - getProcessingVNode, throwNotInFuncError } from './BaseHook'; import { @@ -12,6 +11,7 @@ import { isSame } from '../utils/compare'; import { setStateChange } from '../render/FunctionComponent'; import { getHookStage, HookStage } from './HookStage'; import type { VNode } from '../Types'; +import {getProcessingVNode} from '../GlobalVar'; export function useReducerImpl(reducer: (S, A) => S, initArg: P, init?: (P) => S, isUseState?: boolean): [S, Trigger] | void { diff --git a/libs/horizon/src/renderer/render/BaseComponent.ts b/libs/horizon/src/renderer/render/BaseComponent.ts index 73749909..1ea097d3 100644 --- a/libs/horizon/src/renderer/render/BaseComponent.ts +++ b/libs/horizon/src/renderer/render/BaseComponent.ts @@ -11,6 +11,7 @@ import { setContext, setNamespaceCtx } from '../ContextSaver'; import { FlagUtils } from '../vnode/VNodeFlags'; import {onlyUpdateChildVNodes} from '../vnode/VNodeCreator'; import componentRenders from './index'; +import {setProcessingVNode} from '../GlobalVar'; // 复用vNode时,也需对stack进行处理 function handlerContext(processing: VNode) { @@ -52,7 +53,12 @@ export function captureVNode(processing: VNode): VNode | null { const shouldUpdate = processing.shouldUpdate; processing.shouldUpdate = false; - return component.captureRender(processing, shouldUpdate); + + setProcessingVNode(processing); + const child = component.captureRender(processing, shouldUpdate); + setProcessingVNode(null); + + return child; } export function markRef(processing: VNode) { diff --git a/libs/horizon/src/renderer/render/ClassComponent.ts b/libs/horizon/src/renderer/render/ClassComponent.ts index 2856ecf9..c8a56178 100644 --- a/libs/horizon/src/renderer/render/ClassComponent.ts +++ b/libs/horizon/src/renderer/render/ClassComponent.ts @@ -58,6 +58,8 @@ function mountInstance(ctor, processing: VNode, nextProps: object) { // 构建子节点 function createChildren(clazz: any, processing: VNode) { + processing.isStoreChange = false; + markRef(processing); setProcessingClassVNode(processing); @@ -157,7 +159,7 @@ export function captureRender(processing: VNode): VNode | null { } // 如果捕获了 error,必须更新 const isCatchError = (processing.flags & DidCapture) === DidCapture; - shouldUpdate = isCatchError || shouldUpdate; + shouldUpdate = isCatchError || shouldUpdate || processing.isStoreChange; // 更新ref markRef(processing); diff --git a/libs/horizon/src/renderer/render/FunctionComponent.ts b/libs/horizon/src/renderer/render/FunctionComponent.ts index 8ce11aed..ec03ebb6 100644 --- a/libs/horizon/src/renderer/render/FunctionComponent.ts +++ b/libs/horizon/src/renderer/render/FunctionComponent.ts @@ -55,7 +55,7 @@ export function captureFunctionComponent( processing, ); - // 这里需要判断是否可以复用,因为函数组件比起其他组价,多了context和stateChange两个因素 + // 这里需要判断是否可以复用,因为函数组件比起其他组件,多了context、stateChange、或者store改变了 三个因素 if (isCanReuse && !isStateChange() && !processing.isStoreChange) { FlagUtils.removeFlag(processing, Update); diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index 7201613d..ee639f4a 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -196,6 +196,12 @@ function unmountVNode(vNode: VNode): void { if (instance && typeof instance.componentWillUnmount === 'function' && !vNode.isSuspended) { callComponentWillUnmount(vNode, instance); } + + // HorizonX会在classComponentWillUnmount中清除对VNode的引入用 + if (vNode.classComponentWillUnmount) { + vNode.classComponentWillUnmount(vNode); + vNode.classComponentWillUnmount = null; + } break; } case DomComponent: { diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index 812d2b8e..d3e6c23a 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -78,9 +78,10 @@ export class VNode { belongClassVNode: VNode | null = null; // 记录JSXElement所属class vNode,处理ref的时候使用 - // 状态管理器使用 + // 状态管理器HorizonX使用 isStoreChange: boolean; - functionToObserver: FunctionToObserver | null; // 记录这个函数组件依赖哪些Observer + observers: Set | null = null; // 记录这个函数组件/类组件依赖哪些Observer + classComponentWillUnmount: Function | null; // HorizonX会在classComponentWillUnmount中清除对VNode的引入用 constructor(tag: VNodeTag, props: any, key: null | string, realNode) { this.tag = tag; // 对应组件的类型,比如ClassComponent等 @@ -107,7 +108,8 @@ export class VNode { this.isDepContextChange = false; this.oldHooks = null; this.isStoreChange = false; - this.functionToObserver = null; + this.observers = null; + this.classComponentWillUnmount = null; break; case ClassComponent: this.realNode = null; @@ -119,6 +121,9 @@ export class VNode { this.isDepContextChange = false; this.oldState = null; this.context = null; + this.isStoreChange = false; + this.observers = null; + this.classComponentWillUnmount = null; break; case DomPortal: this.realNode = null;