From 28f0a2b9b6560a54e09ef764bffbc89eb981c0b0 Mon Sep 17 00:00:00 2001 From: * <*> Date: Tue, 18 Apr 2023 11:45:36 +0800 Subject: [PATCH] Match-id-6d0d90f21208e053a7c407bacfa5a3862d7806fd --- .../dom/DOMPropertiesHandler/StyleHandler.ts | 40 ++-- libs/horizon/src/event/EventBinding.ts | 16 +- libs/horizon/src/external/TestUtil.ts | 28 +-- libs/horizon/src/horizonx/devtools/index.ts | 31 +-- .../src/horizonx/proxy/ProxyHandler.ts | 8 +- .../proxy/handlers/ArrayProxyHandler.ts | 68 +++--- .../src/horizonx/proxy/handlers/MapProxy.ts | 136 ++++++------ .../proxy/handlers/ObjectProxyHandler.ts | 42 ++-- .../src/horizonx/proxy/handlers/SetProxy.ts | 91 ++++---- .../src/horizonx/store/StoreHandler.ts | 188 ++++++++-------- libs/horizon/src/renderer/TreeBuilder.ts | 68 +++--- .../src/renderer/diff/nodeDiffComparator.ts | 26 +-- .../src/renderer/submit/LifeCycleHandler.ts | 202 +++++++++--------- 13 files changed, 474 insertions(+), 470 deletions(-) diff --git a/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts b/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts index c3954fa1..3c3a9a24 100644 --- a/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts +++ b/libs/horizon/src/dom/DOMPropertiesHandler/StyleHandler.ts @@ -13,6 +13,26 @@ * See the Mulan PSL v2 for more details. */ +/** + * 不需要加长度单位的 css 属性 + */ +const noUnitCSS = [ + 'animationIterationCount', + 'columnCount', + 'columns', + 'gridArea', + 'fontWeight', + 'lineClamp', + 'lineHeight', + 'opacity', + 'order', + 'orphans', + 'tabSize', + 'widows', + 'zIndex', + 'zoom', +]; + function isNeedUnitCSS(styleName: string) { return !( noUnitCSS.includes(styleName) || @@ -62,23 +82,3 @@ export function setStyles(dom, styles) { } }); } - -/** - * 不需要加长度单位的 css 属性 - */ -const noUnitCSS = [ - 'animationIterationCount', - 'columnCount', - 'columns', - 'gridArea', - 'fontWeight', - 'lineClamp', - 'lineHeight', - 'opacity', - 'order', - 'orphans', - 'tabSize', - 'widows', - 'zIndex', - 'zoom', -]; diff --git a/libs/horizon/src/event/EventBinding.ts b/libs/horizon/src/event/EventBinding.ts index e9d88308..25c52b90 100644 --- a/libs/horizon/src/event/EventBinding.ts +++ b/libs/horizon/src/event/EventBinding.ts @@ -56,6 +56,14 @@ function listenToNativeEvent(nativeEvtName: string, delegatedElement: Element, i return listener; } +// 是否捕获事件 +function isCaptureEvent(horizonEventName) { + if (horizonEventName === 'onLostPointerCapture' || horizonEventName === 'onGotPointerCapture') { + return false; + } + return horizonEventName.slice(-7) === 'Capture'; +} + // 事件懒委托,当用户定义事件后,再进行委托到根节点 export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) { currentRoot.delegatedEvents.add(eventName); @@ -94,14 +102,6 @@ function getNativeEvtName(horizonEventName, capture) { return nativeName.toLowerCase(); } -// 是否捕获事件 -function isCaptureEvent(horizonEventName) { - if (horizonEventName === 'onLostPointerCapture' || horizonEventName === 'onGotPointerCapture') { - return false; - } - return horizonEventName.slice(-7) === 'Capture'; -} - // 封装监听函数 function getWrapperListener(horizonEventName, nativeEvtName, targetElement, listener) { return event => { diff --git a/libs/horizon/src/external/TestUtil.ts b/libs/horizon/src/external/TestUtil.ts index dc9e3dbd..3b5b24ca 100644 --- a/libs/horizon/src/external/TestUtil.ts +++ b/libs/horizon/src/external/TestUtil.ts @@ -23,6 +23,20 @@ interface Thenable { then(resolve: (val?: any) => void, reject: (err: any) => void): void; } +// 防止死循环 +const LOOPING_LIMIT = 50; +let loopingCount = 0; +function callRenderQueue() { + callRenderQueueImmediate(); + + while (hasAsyncEffects() && loopingCount < LOOPING_LIMIT) { + loopingCount++; + runAsyncEffects(); + // effects可能产生刷新任务,这里再执行一次 + callRenderQueueImmediate(); + } +} + // act用于测试,作用是:如果fun触发了刷新(包含了异步刷新),可以保证在act后面的代码是在刷新完成后才执行。 function act(fun: () => void | Thenable): Thenable { const funRet = asyncUpdates(fun); @@ -62,20 +76,6 @@ function act(fun: () => void | Thenable): Thenable { } } -// 防止死循环 -const LOOPING_LIMIT = 50; -let loopingCount = 0; -function callRenderQueue() { - callRenderQueueImmediate(); - - while (hasAsyncEffects() && loopingCount < LOOPING_LIMIT) { - loopingCount++; - runAsyncEffects(); - // effects可能产生刷新任务,这里再执行一次 - callRenderQueueImmediate(); - } -} - export { act }; diff --git a/libs/horizon/src/horizonx/devtools/index.ts b/libs/horizon/src/horizonx/devtools/index.ts index 23bb6000..64a0f189 100644 --- a/libs/horizon/src/horizonx/devtools/index.ts +++ b/libs/horizon/src/horizonx/devtools/index.ts @@ -10,21 +10,6 @@ export function isPanelActive() { return window['__HORIZON_DEV_HOOK__']; } -// serializes store and creates expanded object with baked-in containing current computed values -function makeStoreSnapshot({ type, data }) { - const expanded = {}; - Object.keys(data.store.$c).forEach(key => { - expanded[key] = data.store[key]; - }); - data.store.expanded = expanded; - const snapshot = makeProxySnapshot({ - data, - type, - sessionId, - }); - return snapshot; -} - // safely serializes variables containing values wrapped in Proxy object function getType(value) { if (!value) return 'nullish'; @@ -39,6 +24,7 @@ function getType(value) { if (typeof value === 'object') return 'object'; return 'primitive'; } + function makeProxySnapshot(obj, visited: any[] = []) { const type = getType(obj); let clone; @@ -112,6 +98,21 @@ function makeProxySnapshot(obj, visited: any[] = []) { } } +// serializes store and creates expanded object with baked-in containing current computed values +function makeStoreSnapshot({ type, data }) { + const expanded = {}; + Object.keys(data.store.$c).forEach(key => { + expanded[key] = data.store[key]; + }); + data.store.expanded = expanded; + const snapshot = makeProxySnapshot({ + data, + type, + sessionId, + }); + return snapshot; +} + export const devtools = { // returns vNode id from horizon devtools getVNodeId: vNode => { diff --git a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts index d165675e..be83bee5 100644 --- a/libs/horizon/src/horizonx/proxy/ProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/ProxyHandler.ts @@ -27,6 +27,10 @@ const proxyMap = new WeakMap(); export const hookObserverMap = new WeakMap(); +export function getObserver(rawObj: any): Observer { + return rawObj[OBSERVER_KEY]; +} + export function createProxy(rawObj: any, isHookObserver = true, listener: { current: (...args) => any }): any { // 不是对象(是原始数据类型)不用代理 if (!(rawObj && isObject(rawObj))) { @@ -89,7 +93,3 @@ export function createProxy(rawObj: any, isHookObserver = true, listener: { curr return proxyObj; } - -export function getObserver(rawObj: any): Observer { - return rawObj[OBSERVER_KEY]; -} diff --git a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts index 3938ecab..669f560a 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ArrayProxyHandler.ts @@ -19,6 +19,40 @@ import { resolveMutation } from '../../CommonUtils'; import { isPanelActive } from '../../devtools'; import { OBSERVER_KEY } from '../../Constants'; +function set(rawObj: any[], key: string, value: any, receiver: any) { + const oldValue = rawObj[key]; + const oldLength = rawObj.length; + const newValue = value; + + const oldArray = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; + + const ret = Reflect.set(rawObj, key, newValue, receiver); + + const newLength = rawObj.length; + const observer = getObserver(rawObj); + + const mutation = isPanelActive() ? resolveMutation(oldArray, rawObj) : resolveMutation(null, rawObj); + + if (!isSame(newValue, oldValue)) { + // 值不一样,触发监听器 + if (observer.watchers?.[key]) { + observer.watchers[key].forEach(cb => { + cb(key, oldValue, newValue, mutation); + }); + } + + // 触发属性变化 + observer.setProp(key, mutation); + } + + if (oldLength !== newLength) { + // 触发数组的大小变化 + observer.setProp('length', mutation); + } + + return ret; +} + export function createArrayProxy(rawObj: any[], listener: { current: (...args) => any }): any[] { let listeners = [] as ((...args) => void)[]; @@ -118,37 +152,3 @@ export function createArrayProxy(rawObj: any[], listener: { current: (...args) = return new Proxy(rawObj, handle); } - -function set(rawObj: any[], key: string, value: any, receiver: any) { - const oldValue = rawObj[key]; - const oldLength = rawObj.length; - const newValue = value; - - const oldArray = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; - - const ret = Reflect.set(rawObj, key, newValue, receiver); - - const newLength = rawObj.length; - const observer = getObserver(rawObj); - - const mutation = isPanelActive() ? resolveMutation(oldArray, rawObj) : resolveMutation(null, rawObj); - - if (!isSame(newValue, oldValue)) { - // 值不一样,触发监听器 - if (observer.watchers?.[key]) { - observer.watchers[key].forEach(cb => { - cb(key, oldValue, newValue, mutation); - }); - } - - // 触发属性变化 - observer.setProp(key, mutation); - } - - if (oldLength !== newLength) { - // 触发数组的大小变化 - observer.setProp('length', mutation); - } - - return ret; -} diff --git a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts index d5ed5d37..dd51f07f 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/MapProxy.ts @@ -25,48 +25,6 @@ export function createMapProxy(rawObj: Object, hookObserver = true, listener: { let oldData: [any, any][] = []; let proxies = new Map(); - function get(rawObj: { size: number }, key: any, receiver: any): any { - if (key === 'size') { - return size(rawObj); - } - - if (key === 'get') { - return getFun.bind(null, rawObj); - } - - if (Object.prototype.hasOwnProperty.call(handler, key)) { - const value = Reflect.get(handler, key, receiver); - return value.bind(null, rawObj); - } - - if (key === 'watch') { - const observer = getObserver(rawObj); - - return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { - if (!observer.watchers[prop]) { - observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; - } - observer.watchers[prop].push(handler); - return () => { - observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); - }; - }; - } - - if (key === 'addListener') { - return listener => { - listeners.push(listener); - }; - } - - if (key === 'removeListener') { - return listener => { - listeners = listeners.filter(item => item != listener); - }; - } - - return Reflect.get(rawObj, key, receiver); - } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function getFun(rawObj: { get: (key: any) => any; has: (key: any) => boolean }, key: any) { const keyProxy = rawObj.has(key) ? key : proxies.get(key); @@ -211,31 +169,6 @@ export function createMapProxy(rawObj: Object, hookObserver = true, listener: { return false; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - function size(rawObj: { size: number }) { - const observer = getObserver(rawObj); - observer.useProp(COLLECTION_CHANGE); - return rawObj.size; - } - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.keys(), 'keys'); - } - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.values(), 'values'); - } - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.entries(), 'entries'); - } - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - function forOf(rawObj: { - entries: () => { next: () => { value: any; done: boolean } }; - values: () => { next: () => { value: any; done: boolean } }; - }) { - return wrapIterator(rawObj, rawObj.entries(), 'entries'); - } - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function forEach( rawObj: { forEach: (callback: (value: any, key: any) => void) => void }, callback: (valProxy: any, keyProxy: any, rawObj: any) => void @@ -354,6 +287,75 @@ export function createMapProxy(rawObj: Object, hookObserver = true, listener: { }, }; } + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function size(rawObj: { size: number }) { + const observer = getObserver(rawObj); + observer.useProp(COLLECTION_CHANGE); + return rawObj.size; + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.keys(), 'keys'); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.values(), 'values'); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.entries(), 'entries'); + } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + function forOf(rawObj: { + entries: () => { next: () => { value: any; done: boolean } }; + values: () => { next: () => { value: any; done: boolean } }; + }) { + return wrapIterator(rawObj, rawObj.entries(), 'entries'); + } + + function get(rawObj: { size: number }, key: any, receiver: any): any { + if (key === 'size') { + return size(rawObj); + } + + if (key === 'get') { + return getFun.bind(null, rawObj); + } + + if (Object.prototype.hasOwnProperty.call(handler, key)) { + const value = Reflect.get(handler, key, receiver); + return value.bind(null, rawObj); + } + + if (key === 'watch') { + const observer = getObserver(rawObj); + + return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { + if (!observer.watchers[prop]) { + observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; + } + observer.watchers[prop].push(handler); + return () => { + observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); + }; + }; + } + + if (key === 'addListener') { + return listener => { + listeners.push(listener); + }; + } + + if (key === 'removeListener') { + return listener => { + listeners = listeners.filter(item => item != listener); + }; + } + + return Reflect.get(rawObj, key, receiver); + } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// const handler = { get, diff --git a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts index f1ac97b7..0afa6583 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/ObjectProxyHandler.ts @@ -18,6 +18,27 @@ import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler'; import { OBSERVER_KEY } from '../../Constants'; import { isPanelActive } from '../../devtools'; +function set(rawObj: object, key: string, value: any, receiver: any): boolean { + const oldObject = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; + const observer = getObserver(rawObj); + + const oldValue = rawObj[key]; + const newValue = value; + + const ret = Reflect.set(rawObj, key, newValue, receiver); + const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : resolveMutation(null, rawObj); + + if (!isSame(newValue, oldValue)) { + if (observer.watchers?.[key]) { + observer.watchers[key].forEach(cb => { + cb(key, oldValue, newValue, mutation); + }); + } + observer.setProp(key, mutation); + } + return ret; +} + export function createObjectProxy( rawObj: T, singleLevel = false, @@ -99,24 +120,3 @@ export function createObjectProxy( return proxy; } - -function set(rawObj: object, key: string, value: any, receiver: any): boolean { - const oldObject = isPanelActive() ? JSON.parse(JSON.stringify(rawObj)) : null; - const observer = getObserver(rawObj); - - const oldValue = rawObj[key]; - const newValue = value; - - const ret = Reflect.set(rawObj, key, newValue, receiver); - const mutation = isPanelActive() ? resolveMutation(oldObject, rawObj) : resolveMutation(null, rawObj); - - if (!isSame(newValue, oldValue)) { - if (observer.watchers?.[key]) { - observer.watchers[key].forEach(cb => { - cb(key, oldValue, newValue, mutation); - }); - } - observer.setProp(key, mutation); - } - return ret; -} diff --git a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts index 6a4401fa..b0409ad6 100644 --- a/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts +++ b/libs/horizon/src/horizonx/proxy/handlers/SetProxy.ts @@ -26,43 +26,6 @@ export function createSetProxy( let listeners: ((mutation) => {})[] = []; let proxies = new WeakMap(); - function get(rawObj: { size: number }, key: any, receiver: any): any { - if (Object.prototype.hasOwnProperty.call(handler, key)) { - const value = Reflect.get(handler, key, receiver); - return value.bind(null, rawObj); - } - - if (key === 'size') { - return size(rawObj); - } - - if (key === 'addListener') { - return listener => { - listeners.push(listener); - }; - } - - if (key === 'removeListener') { - return listener => { - listeners = listeners.filter(item => item != listener); - }; - } - if (key === 'watch') { - const observer = getObserver(rawObj); - - return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { - if (!observer.watchers[prop]) { - observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; - } - observer.watchers[prop].push(handler); - return () => { - observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); - }; - }; - } - return Reflect.get(rawObj, key, receiver); - } - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Set的add方法 function add(rawObj: { add: (any) => void; has: (any) => boolean; values: () => any[] }, value: any): Object { if (!rawObj.has(proxies.get(value))) { @@ -166,17 +129,43 @@ export function createSetProxy( return rawObj.size; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.keys()); - } + function get(rawObj: { size: number }, key: any, receiver: any): any { + if (Object.prototype.hasOwnProperty.call(handler, key)) { + const value = Reflect.get(handler, key, receiver); + return value.bind(null, rawObj); + } - function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.values()); - } + if (key === 'size') { + return size(rawObj); + } - function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) { - return wrapIterator(rawObj, rawObj.entries()); + if (key === 'addListener') { + return listener => { + listeners.push(listener); + }; + } + + if (key === 'removeListener') { + return listener => { + listeners = listeners.filter(item => item != listener); + }; + } + if (key === 'watch') { + const observer = getObserver(rawObj); + + return (prop: any, handler: (key: string, oldValue: any, newValue: any) => void) => { + if (!observer.watchers[prop]) { + observer.watchers[prop] = [] as ((key: string, oldValue: any, newValue: any) => void)[]; + } + observer.watchers[prop].push(handler); + return () => { + observer.watchers[prop] = observer.watchers[prop].filter(cb => cb !== handler); + }; + }; + } + return Reflect.get(rawObj, key, receiver); } + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// function wrapIterator(rawObj: Object, rawIt: { next: () => { value: any; done: boolean } }) { const observer = getObserver(rawObj); @@ -224,6 +213,18 @@ export function createSetProxy( }; } + function keys(rawObj: { keys: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.keys()); + } + + function values(rawObj: { values: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.values()); + } + + function entries(rawObj: { entries: () => { next: () => { value: any; done: boolean } } }) { + return wrapIterator(rawObj, rawObj.entries()); + } + function forOf(rawObj: { entries: () => { next: () => { value: any; done: boolean } }; values: () => { next: () => { value: any; done: boolean } }; diff --git a/libs/horizon/src/horizonx/store/StoreHandler.ts b/libs/horizon/src/horizonx/store/StoreHandler.ts index 781deef4..2afd33f2 100644 --- a/libs/horizon/src/horizonx/store/StoreHandler.ts +++ b/libs/horizon/src/horizonx/store/StoreHandler.ts @@ -53,6 +53,100 @@ const idGenerator = { const storeMap = new Map>(); +// 通过该方法执行store.$queue中的action +function tryNextAction(storeObj, proxyObj, config, plannedActions) { + if (!plannedActions.length) { + if (proxyObj.$pending) { + const timestamp = Date.now(); + const duration = timestamp - proxyObj.$pending; + proxyObj.$pending = false; + devtools.emit(QUEUE_FINISHED, { + store: storeObj, + endedAt: timestamp, + duration, + }); + } + return; + } + + const nextAction = plannedActions.shift()!; + const result = config.actions + ? config.actions[nextAction.action].bind(storeObj, proxyObj)(...nextAction.payload) + : undefined; + + if (isPromise(result)) { + result.then(value => { + nextAction.resolve(value); + tryNextAction(storeObj, proxyObj, config, plannedActions); + }); + } else { + nextAction.resolve(result); + tryNextAction(storeObj, proxyObj, config, plannedActions); + } +} + +// 删除Observers中保存的这个VNode的相关数据 +export function clearVNodeObservers(vNode: VNode) { + if (!vNode.observers) { + return; + } + + vNode.observers.forEach(observer => { + observer.clearByVNode(vNode); + }); + + vNode.observers.clear(); +} + +// 注册VNode销毁时的清理动作 +function registerDestroyFunction() { + const processingVNode = getProcessingVNode(); + + // 获取不到当前运行的VNode,说明不在组件中运行,属于非法场景 + if (!processingVNode) { + return; + } + + if (!processingVNode.observers) { + processingVNode.observers = new Set(); + } + + // 函数组件 + if (processingVNode.tag === FunctionComponent) { + const vNodeRef = useRef(processingVNode); + + useEffect(() => { + return () => { + clearVNodeObservers(vNodeRef.current); + vNodeRef.current.observers = null; + }; + }, []); + } else if (processingVNode.tag === ClassComponent) { + // 类组件 + if (!processingVNode.classComponentWillUnmount) { + processingVNode.classComponentWillUnmount = vNode => { + clearVNodeObservers(vNode); + vNode.observers = null; + }; + } + } +} + +// createStore返回的是一个getStore的函数,这个函数必须要在组件(函数/类组件)里面被执行,因为要注册VNode销毁时的清理动作 +function createGetStore, C extends UserComputedValues>( + storeObj: StoreObj +): () => StoreObj { + const getStore = () => { + if (!storeObj.$config.options?.isReduxAdapter) { + registerDestroyFunction(); + } + + return storeObj; + }; + + return getStore; +} + export function createStore, C extends UserComputedValues>( config: StoreConfig ): () => StoreObj { @@ -216,100 +310,6 @@ export function createStore, C extend return createGetStore(storeObj); } -// 通过该方法执行store.$queue中的action -function tryNextAction(storeObj, proxyObj, config, plannedActions) { - if (!plannedActions.length) { - if (proxyObj.$pending) { - const timestamp = Date.now(); - const duration = timestamp - proxyObj.$pending; - proxyObj.$pending = false; - devtools.emit(QUEUE_FINISHED, { - store: storeObj, - endedAt: timestamp, - duration, - }); - } - return; - } - - const nextAction = plannedActions.shift()!; - const result = config.actions - ? config.actions[nextAction.action].bind(storeObj, proxyObj)(...nextAction.payload) - : undefined; - - if (isPromise(result)) { - result.then(value => { - nextAction.resolve(value); - tryNextAction(storeObj, proxyObj, config, plannedActions); - }); - } else { - nextAction.resolve(result); - tryNextAction(storeObj, proxyObj, config, plannedActions); - } -} - -// createStore返回的是一个getStore的函数,这个函数必须要在组件(函数/类组件)里面被执行,因为要注册VNode销毁时的清理动作 -function createGetStore, C extends UserComputedValues>( - storeObj: StoreObj -): () => StoreObj { - const getStore = () => { - if (!storeObj.$config.options?.isReduxAdapter) { - registerDestroyFunction(); - } - - return storeObj; - }; - - return getStore; -} - -// 删除Observers中保存的这个VNode的相关数据 -export function clearVNodeObservers(vNode: VNode) { - if (!vNode.observers) { - return; - } - - vNode.observers.forEach(observer => { - observer.clearByVNode(vNode); - }); - - vNode.observers.clear(); -} - -// 注册VNode销毁时的清理动作 -function registerDestroyFunction() { - const processingVNode = getProcessingVNode(); - - // 获取不到当前运行的VNode,说明不在组件中运行,属于非法场景 - if (!processingVNode) { - return; - } - - if (!processingVNode.observers) { - processingVNode.observers = new Set(); - } - - // 函数组件 - if (processingVNode.tag === FunctionComponent) { - const vNodeRef = useRef(processingVNode); - - useEffect(() => { - return () => { - clearVNodeObservers(vNodeRef.current); - vNodeRef.current.observers = null; - }; - }, []); - } else if (processingVNode.tag === ClassComponent) { - // 类组件 - if (!processingVNode.classComponentWillUnmount) { - processingVNode.classComponentWillUnmount = vNode => { - clearVNodeObservers(vNode); - vNode.observers = null; - }; - } - } -} - // 函数组件中使用的hook export function useStore, C extends UserComputedValues>( id: string diff --git a/libs/horizon/src/renderer/TreeBuilder.ts b/libs/horizon/src/renderer/TreeBuilder.ts index 7d37de2f..eb20b0ef 100644 --- a/libs/horizon/src/renderer/TreeBuilder.ts +++ b/libs/horizon/src/renderer/TreeBuilder.ts @@ -228,6 +228,40 @@ export function calcStartUpdateVNode(treeRoot: VNode) { return node; } +// 在局部更新时,从上到下恢复父节点的context和PortalStack +function recoverTreeContext(vNode: VNode) { + const contextProviders: VNode[] = []; + let parent = vNode.parent; + while (parent !== null) { + if (parent.tag === ContextProvider) { + contextProviders.unshift(parent); + } + if (parent.tag === DomPortal) { + pushCurrentRoot(parent); + } + parent = parent.parent; + } + + contextProviders.forEach(node => { + setContext(node, node.props.value); + }); +} + +// 在局部更新时,从下到上重置父节点的context +function resetTreeContext(vNode: VNode) { + let parent = vNode.parent; + + while (parent !== null) { + if (parent.tag === ContextProvider) { + resetContext(parent); + } + if (parent.tag === DomPortal) { + popCurrentRoot(); + } + parent = parent.parent; + } +} + // ============================== 深度遍历 ============================== function buildVNodeTree(treeRoot: VNode) { const preMode = copyExecuteMode(); @@ -299,40 +333,6 @@ function buildVNodeTree(treeRoot: VNode) { setExecuteMode(preMode); } -// 在局部更新时,从上到下恢复父节点的context和PortalStack -function recoverTreeContext(vNode: VNode) { - const contextProviders: VNode[] = []; - let parent = vNode.parent; - while (parent !== null) { - if (parent.tag === ContextProvider) { - contextProviders.unshift(parent); - } - if (parent.tag === DomPortal) { - pushCurrentRoot(parent); - } - parent = parent.parent; - } - - contextProviders.forEach(node => { - setContext(node, node.props.value); - }); -} - -// 在局部更新时,从下到上重置父节点的context -function resetTreeContext(vNode: VNode) { - let parent = vNode.parent; - - while (parent !== null) { - if (parent.tag === ContextProvider) { - resetContext(parent); - } - if (parent.tag === DomPortal) { - popCurrentRoot(); - } - parent = parent.parent; - } -} - // 总体任务入口 function renderFromRoot(treeRoot) { runAsyncEffects(); diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts index 56753063..61706bea 100644 --- a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts +++ b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts @@ -231,6 +231,19 @@ function getOldNodeFromMap(nodeMap: Map, newIdx: number, return null; } +// 设置vNode中的cIndex属性,cIndex是节点在children中的位置 +function setVNodesCIndex(startChild: VNode | null, startIdx: number) { + let node: VNode | null = startChild; + let idx = startIdx; + + while (node !== null) { + node.cIndex = idx; + markVNodePath(node); + node = node.next; + idx++; + } +} + // diff数组类型的节点,核心算法 function diffArrayNodesHandler(parentNode: VNode, firstChild: VNode | null, newChildren: Array): VNode | null { let resultingFirstChild: VNode | null = null; @@ -478,19 +491,6 @@ function diffArrayNodesHandler(parentNode: VNode, firstChild: VNode | null, newC return resultingFirstChild; } -// 设置vNode中的cIndex属性,cIndex是节点在children中的位置 -function setVNodesCIndex(startChild: VNode | null, startIdx: number) { - let node: VNode | null = startChild; - let idx = startIdx; - - while (node !== null) { - node.cIndex = idx; - markVNodePath(node); - node = node.next; - idx++; - } -} - // 新节点是迭代器类型 function diffIteratorNodesHandler( parentNode: VNode, diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index ebf73031..056f18f9 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -182,47 +182,6 @@ function detachRef(vNode: VNode, isOldRef?: boolean) { handleRef(vNode, ref, null); } -// 卸载一个vNode,不会递归 -function unmountVNode(vNode: VNode): void { - switch (vNode.tag) { - case FunctionComponent: - case ForwardRef: - case MemoComponent: { - callEffectRemove(vNode); - break; - } - case ClassComponent: { - detachRef(vNode); - - const instance = vNode.realNode; - // 当constructor中抛出异常时,instance会是null,这里判断一下instance是否为空 - // suspense打断时不需要触发WillUnmount - 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: { - detachRef(vNode); - break; - } - case DomPortal: { - // 这里会递归 - unmountDomComponents(vNode); - break; - } - default: { - break; - } - } -} - // 卸载vNode,递归遍历子vNode function unmountNestedVNodes(vNode: VNode): void { travelVNodeTree( @@ -238,59 +197,6 @@ function unmountNestedVNodes(vNode: VNode): void { ); } -function submitAddition(vNode: VNode): void { - let parent = vNode.parent; - let parentDom; - let tag; - while (parent !== null) { - tag = parent.tag; - if (tag === DomComponent || tag === TreeRoot || tag === DomPortal) { - parentDom = parent.realNode; - break; - } - parent = parent.parent; - } - - if ((parent.flags & ResetText) === ResetText) { - // 在insert之前先reset - clearText(parentDom); - FlagUtils.removeFlag(parent, ResetText); - } - - if ((vNode.flags & DirectAddition) === DirectAddition) { - insertOrAppendPlacementNode(vNode, null, parentDom); - FlagUtils.removeFlag(vNode, DirectAddition); - return; - } - const before = getSiblingDom(vNode); - insertOrAppendPlacementNode(vNode, before, parentDom); -} - -function insertOrAppendPlacementNode(node: VNode, beforeDom: Element | null, parent: Element | Container): void { - const { tag, realNode } = node; - - if (isDomVNode(node)) { - insertDom(parent, realNode, beforeDom); - } else if (tag === DomPortal) { - // 这里不做处理,直接在portal中处理 - } else { - // 插入子节点们 - let child = node.child; - while (child !== null) { - insertOrAppendPlacementNode(child, beforeDom, parent); - child = child.next; - } - } -} - -function insertDom(parent, realNode, beforeDom) { - if (beforeDom) { - insertDomBefore(parent, realNode, beforeDom); - } else { - appendChildElement(parent, realNode); - } -} - // 遍历所有子节点:删除dom节点,detach ref 和 调用componentWillUnmount() function unmountDomComponents(vNode: VNode): void { let currentParentIsValid = false; @@ -342,6 +248,100 @@ function unmountDomComponents(vNode: VNode): void { ); } +// 卸载一个vNode,不会递归 +function unmountVNode(vNode: VNode): void { + switch (vNode.tag) { + case FunctionComponent: + case ForwardRef: + case MemoComponent: { + callEffectRemove(vNode); + break; + } + case ClassComponent: { + detachRef(vNode); + + const instance = vNode.realNode; + // 当constructor中抛出异常时,instance会是null,这里判断一下instance是否为空 + // suspense打断时不需要触发WillUnmount + 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: { + detachRef(vNode); + break; + } + case DomPortal: { + // 这里会递归 + unmountDomComponents(vNode); + break; + } + default: { + break; + } + } +} + +function insertDom(parent, realNode, beforeDom) { + if (beforeDom) { + insertDomBefore(parent, realNode, beforeDom); + } else { + appendChildElement(parent, realNode); + } +} + +function insertOrAppendPlacementNode(node: VNode, beforeDom: Element | null, parent: Element | Container): void { + const { tag, realNode } = node; + + if (isDomVNode(node)) { + insertDom(parent, realNode, beforeDom); + } else if (tag === DomPortal) { + // 这里不做处理,直接在portal中处理 + } else { + // 插入子节点们 + let child = node.child; + while (child !== null) { + insertOrAppendPlacementNode(child, beforeDom, parent); + child = child.next; + } + } +} + +function submitAddition(vNode: VNode): void { + let parent = vNode.parent; + let parentDom; + let tag; + while (parent !== null) { + tag = parent.tag; + if (tag === DomComponent || tag === TreeRoot || tag === DomPortal) { + parentDom = parent.realNode; + break; + } + parent = parent.parent; + } + + if ((parent.flags & ResetText) === ResetText) { + // 在insert之前先reset + clearText(parentDom); + FlagUtils.removeFlag(parent, ResetText); + } + + if ((vNode.flags & DirectAddition) === DirectAddition) { + insertOrAppendPlacementNode(vNode, null, parentDom); + FlagUtils.removeFlag(vNode, DirectAddition); + return; + } + const before = getSiblingDom(vNode); + insertOrAppendPlacementNode(vNode, before, parentDom); +} + function submitClear(vNode: VNode): void { const realNode = vNode.realNode; const cloneDom = realNode.cloneNode(false); // 复制节点后horizon添加给dom的属性未能复制 @@ -397,6 +397,13 @@ function submitDeletion(vNode: VNode): void { clearVNode(vNode); } +function submitSuspenseComponent(vNode: VNode) { + const { childStatus } = vNode.suspenseState; + if (childStatus !== SuspenseChildStatus.Init) { + hideOrUnhideAllChildren(vNode.child, childStatus === SuspenseChildStatus.ShowFallback); + } +} + function submitUpdate(vNode: VNode): void { switch (vNode.tag) { case FunctionComponent: @@ -422,13 +429,6 @@ function submitUpdate(vNode: VNode): void { } } -function submitSuspenseComponent(vNode: VNode) { - const { childStatus } = vNode.suspenseState; - if (childStatus !== SuspenseChildStatus.Init) { - hideOrUnhideAllChildren(vNode.child, childStatus === SuspenseChildStatus.ShowFallback); - } -} - function submitResetTextContent(vNode: VNode) { clearText(vNode.realNode); }