From 356874599a8be56289e614c47d836d6465a5deb1 Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 25 Jan 2022 09:33:01 +0800 Subject: [PATCH 1/6] Match-id-b3144e462876f69f20c7c56f50a88fa9cd532298 --- libs/horizon/src/dom/DOMInternalKeys.ts | 2 +- .../src/renderer/diff/nodeDiffComparator.ts | 10 +++- .../src/renderer/submit/LifeCycleHandler.ts | 53 ++++++++++++++++++- libs/horizon/src/renderer/submit/Submit.ts | 7 ++- libs/horizon/src/renderer/vnode/VNode.ts | 3 +- libs/horizon/src/renderer/vnode/VNodeFlags.ts | 7 ++- 6 files changed, 74 insertions(+), 8 deletions(-) diff --git a/libs/horizon/src/dom/DOMInternalKeys.ts b/libs/horizon/src/dom/DOMInternalKeys.ts index dec6fed1..724ae509 100644 --- a/libs/horizon/src/dom/DOMInternalKeys.ts +++ b/libs/horizon/src/dom/DOMInternalKeys.ts @@ -16,7 +16,7 @@ import { const prefix = '_horizon'; -const internalKeys = { +export const internalKeys = { VNode: `${prefix}VNode`, props: `${prefix}Props`, events: `${prefix}Events`, diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts index cf2a7993..b805f71b 100644 --- a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts +++ b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts @@ -1,7 +1,7 @@ import type { VNode } from '../Types'; import { FlagUtils } from '../vnode/VNodeFlags'; import { TYPE_COMMON_ELEMENT, TYPE_FRAGMENT, TYPE_PORTAL } from '../../external/JSXElementType'; -import { DomText, DomPortal, Fragment } from '../vnode/VNodeTags'; +import { DomText, DomPortal, Fragment, DomComponent } from '../vnode/VNodeTags'; import {updateVNode, createVNode, createVNodeFromElement, updateVNodePath} from '../vnode/VNodeCreator'; import { isSameType, @@ -320,7 +320,13 @@ function diffArrayNodes( // 3. 新节点已经处理完成 if (leftIdx === rightIdx) { if (isComparing) { - deleteVNodes(parentNode, oldNode, rightEndOldNode); + if (firstChild && parentNode.tag === DomComponent && newChildren.length === 0) { + // if (false) { + FlagUtils.markClear(parentNode); + parentNode.ClearChild = firstChild; + } else { + deleteVNodes(parentNode, oldNode, rightEndOldNode); + } } if (rightNewNode) { diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index 78a308d4..190763cf 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -17,7 +17,7 @@ import { SuspenseComponent, MemoComponent, } from '../vnode/VNodeTags'; -import { FlagUtils, ResetText } from '../vnode/VNodeFlags'; +import { FlagUtils, ResetText, Clear } from '../vnode/VNodeFlags'; import { mergeDefaultProps } from '../render/LazyComponent'; import { submitDomUpdate, @@ -43,6 +43,7 @@ import { findDomParent, getSiblingDom, } from '../vnode/VNodeUtils'; import { shouldAutoFocus } from '../../dom/utils/Common'; +import { internalKeys } from '../../dom/DOMInternalKeys'; function callComponentWillUnmount(vNode: VNode, instance: any) { try { @@ -307,6 +308,55 @@ function unmountDomComponents(vNode: VNode): void { }); } +function submitClear(vNode: VNode): void { + const realNode = vNode.realNode; + const cloneDom = realNode.cloneNode(false); // 复制节点后horizon的两个属性消失 + cloneDom[internalKeys.VNode] = realNode[internalKeys.VNode]; + cloneDom[internalKeys.props] = realNode[internalKeys.props]; + // 删除节点 + let currentParentIsValid = false; + + // 这两个变量要一起更新 + let currentParent; + + travelVNodeTree(vNode, (node) => { + if (!currentParentIsValid) { + const parentObj = findDomParent(node); + currentParent = parentObj.parentDom; + currentParentIsValid = true; + } + const tag = node.tag; + if (tag === DomComponent || tag === DomText) { + // 卸载子vNode,递归遍历子vNode + unmountNestedVNodes(node.ClearChild); // node.child 为空,需要添加原有的child + + // 在所有子项都卸载后,删除dom树中的节点 + removeChildDom(currentParent, node.realNode); + console.log(currentParent); + currentParent.append(cloneDom); + vNode.realNode = cloneDom; + FlagUtils.removeFlag(vNode, Clear); + vNode.ClearChild = null; + } else if (tag === DomPortal) { + console.log(DomPortal); + if (node.child !== null) { + currentParent = node.outerDom; + } + } else { + unmountVNode(node); + } + }, (node) => { + // 如果是dom不用再遍历child + const tag = node.tag; + return tag === DomComponent || tag === DomText; + }, null, (node) => { + if (node.tag === DomPortal) { + // 当离开portal,需要重新设置parent + currentParentIsValid = false; + } + }); +} + function submitDeletion(vNode: VNode): void { // 遍历所有子节点:删除dom节点,detach ref 和 调用componentWillUnmount() unmountDomComponents(vNode); @@ -353,6 +403,7 @@ export { submitResetTextContent, submitAddition, submitDeletion, + submitClear, submitUpdate, callAfterSubmitLifeCycles, attachRef, diff --git a/libs/horizon/src/renderer/submit/Submit.ts b/libs/horizon/src/renderer/submit/Submit.ts index e76f0b97..446569bb 100644 --- a/libs/horizon/src/renderer/submit/Submit.ts +++ b/libs/horizon/src/renderer/submit/Submit.ts @@ -9,7 +9,7 @@ import { attachRef, callAfterSubmitLifeCycles, callBeforeSubmitLifeCycles, submitDeletion, submitAddition, - submitResetTextContent, submitUpdate, detachRef, + submitResetTextContent, submitUpdate, detachRef, submitClear, } from './LifeCycleHandler'; import {tryRenderRoot, setProcessing} from '../TreeBuilder'; import { @@ -121,7 +121,7 @@ function submit(dirtyNodes: Array) { } } - const {Addition, Update, Deletion} = node.flags; + const {Addition, Update, Deletion, Clear} = node.flags; if (Addition && Update) { // Addition submitAddition(node); @@ -138,6 +138,9 @@ function submit(dirtyNodes: Array) { } else if (Deletion) { submitDeletion(node); } + if (Clear) { + submitClear(node); + } } } catch (error) { throwIfTrue(node === null, 'Should be working on an effect.'); diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index 71c0856e..9cac9cc2 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -60,8 +60,9 @@ export class VNode { Interrupted?: boolean; ShouldCapture?: boolean; ForceUpdate?: boolean; + Clear?: boolean; } = {}; - + ClearChild: VNode|null = null; // one tree相关属性 isCreated: boolean = true; oldHooks: Array> = []; // 保存上一次执行的hook diff --git a/libs/horizon/src/renderer/vnode/VNodeFlags.ts b/libs/horizon/src/renderer/vnode/VNodeFlags.ts index 62e02be1..449d6baa 100644 --- a/libs/horizon/src/renderer/vnode/VNodeFlags.ts +++ b/libs/horizon/src/renderer/vnode/VNodeFlags.ts @@ -18,9 +18,10 @@ export const Interrupted = 'Interrupted'; export const ShouldCapture = 'ShouldCapture'; // For suspense export const ForceUpdate = 'ForceUpdate'; +export const Clear = 'Clear'; const FlagArr = [Addition, Update, Deletion, ResetText, Callback, - DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate]; + DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate, Clear]; const LifecycleEffectArr = [Update, Callback, Ref, Snapshot]; @@ -90,5 +91,9 @@ export class FlagUtils { static markForceUpdate(node: VNode) { node.flags.ForceUpdate = true; } + + static markClear(node: VNode) { + node.flags.Clear = true; + } } From a68c3b9897568eb318c3e8e4dfc8a8f7b617c56d Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 25 Jan 2022 11:38:32 +0800 Subject: [PATCH 2/6] Match-id-5684643f375c3d5f9612e974f8374ae4a3548172 --- .../src/renderer/diff/nodeDiffComparator.ts | 2 +- .../src/renderer/submit/LifeCycleHandler.ts | 54 +++++-------------- libs/horizon/src/renderer/vnode/VNode.ts | 2 +- 3 files changed, 15 insertions(+), 43 deletions(-) diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts index b805f71b..dad4f3a3 100644 --- a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts +++ b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts @@ -323,7 +323,7 @@ function diffArrayNodes( if (firstChild && parentNode.tag === DomComponent && newChildren.length === 0) { // if (false) { FlagUtils.markClear(parentNode); - parentNode.ClearChild = firstChild; + parentNode.clearChild = firstChild; } else { deleteVNodes(parentNode, oldNode, rightEndOldNode); } diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index 190763cf..0fa7bcaa 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -313,48 +313,20 @@ function submitClear(vNode: VNode): void { const cloneDom = realNode.cloneNode(false); // 复制节点后horizon的两个属性消失 cloneDom[internalKeys.VNode] = realNode[internalKeys.VNode]; cloneDom[internalKeys.props] = realNode[internalKeys.props]; - // 删除节点 - let currentParentIsValid = false; - // 这两个变量要一起更新 - let currentParent; - - travelVNodeTree(vNode, (node) => { - if (!currentParentIsValid) { - const parentObj = findDomParent(node); - currentParent = parentObj.parentDom; - currentParentIsValid = true; - } - const tag = node.tag; - if (tag === DomComponent || tag === DomText) { - // 卸载子vNode,递归遍历子vNode - unmountNestedVNodes(node.ClearChild); // node.child 为空,需要添加原有的child - - // 在所有子项都卸载后,删除dom树中的节点 - removeChildDom(currentParent, node.realNode); - console.log(currentParent); - currentParent.append(cloneDom); - vNode.realNode = cloneDom; - FlagUtils.removeFlag(vNode, Clear); - vNode.ClearChild = null; - } else if (tag === DomPortal) { - console.log(DomPortal); - if (node.child !== null) { - currentParent = node.outerDom; - } - } else { - unmountVNode(node); - } - }, (node) => { - // 如果是dom不用再遍历child - const tag = node.tag; - return tag === DomComponent || tag === DomText; - }, null, (node) => { - if (node.tag === DomPortal) { - // 当离开portal,需要重新设置parent - currentParentIsValid = false; - } - }); + const parentObj = findDomParent(vNode); + const currentParent = parentObj.parentDom; + const clearChild = vNode.clearChild as VNode; + // 卸载子vNode,递归遍历子vNode + unmountNestedVNodes(clearChild); // node.child 为空,需要添加原有的child + + // 在所有子项都卸载后,删除dom树中的节点 + removeChildDom(currentParent, vNode.realNode); + clearVNode(clearChild); + currentParent.append(cloneDom); + vNode.realNode = cloneDom; + FlagUtils.removeFlag(vNode, Clear); + vNode.clearChild = null; } function submitDeletion(vNode: VNode): void { diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index 9cac9cc2..82b4ae1e 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -62,7 +62,7 @@ export class VNode { ForceUpdate?: boolean; Clear?: boolean; } = {}; - ClearChild: VNode|null = null; + clearChild: VNode|null = null; // one tree相关属性 isCreated: boolean = true; oldHooks: Array> = []; // 保存上一次执行的hook From b9a2d4067dd96b9e9ac91ad06ec938e88a17846c Mon Sep 17 00:00:00 2001 From: * <8> Date: Tue, 25 Jan 2022 11:49:16 +0800 Subject: [PATCH 3/6] Match-id-30c3c11db166222cef6d0b38a690afdd4a0fbc80 --- libs/horizon/src/renderer/submit/LifeCycleHandler.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index 0fa7bcaa..398709df 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -43,7 +43,6 @@ import { findDomParent, getSiblingDom, } from '../vnode/VNodeUtils'; import { shouldAutoFocus } from '../../dom/utils/Common'; -import { internalKeys } from '../../dom/DOMInternalKeys'; function callComponentWillUnmount(vNode: VNode, instance: any) { try { @@ -311,8 +310,12 @@ function unmountDomComponents(vNode: VNode): void { function submitClear(vNode: VNode): void { const realNode = vNode.realNode; const cloneDom = realNode.cloneNode(false); // 复制节点后horizon的两个属性消失 - cloneDom[internalKeys.VNode] = realNode[internalKeys.VNode]; - cloneDom[internalKeys.props] = realNode[internalKeys.props]; + const customizeKeys = Object.keys; + const keyLength = customizeKeys.length; + for(let i = 0; i < keyLength; i++) { + const key = customizeKeys[i]; + cloneDom[key] = realNode(key); + } const parentObj = findDomParent(vNode); const currentParent = parentObj.parentDom; From dea643344b9ff36f193edc10ca043e5c3447cf60 Mon Sep 17 00:00:00 2001 From: * <8> Date: Wed, 26 Jan 2022 10:14:26 +0800 Subject: [PATCH 4/6] Match-id-14c6d77f9988d9f6f8d9623e58e234decb7b663d --- libs/horizon/src/renderer/diff/nodeDiffComparator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts index dad4f3a3..b1f1203b 100644 --- a/libs/horizon/src/renderer/diff/nodeDiffComparator.ts +++ b/libs/horizon/src/renderer/diff/nodeDiffComparator.ts @@ -321,7 +321,6 @@ function diffArrayNodes( if (leftIdx === rightIdx) { if (isComparing) { if (firstChild && parentNode.tag === DomComponent && newChildren.length === 0) { - // if (false) { FlagUtils.markClear(parentNode); parentNode.clearChild = firstChild; } else { From 6e19d6b6af2b9896887b735ae6a000ccf08710b2 Mon Sep 17 00:00:00 2001 From: * <8> Date: Wed, 26 Jan 2022 10:45:38 +0800 Subject: [PATCH 5/6] Match-id-eb73774c1a152584504f6ce5531febfedfcf3b83 --- .../src/renderer/submit/LifeCycleHandler.ts | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts index 398709df..68503458 100644 --- a/libs/horizon/src/renderer/submit/LifeCycleHandler.ts +++ b/libs/horizon/src/renderer/submit/LifeCycleHandler.ts @@ -309,23 +309,34 @@ function unmountDomComponents(vNode: VNode): void { function submitClear(vNode: VNode): void { const realNode = vNode.realNode; - const cloneDom = realNode.cloneNode(false); // 复制节点后horizon的两个属性消失 - const customizeKeys = Object.keys; + const cloneDom = realNode.cloneNode(false); // 复制节点后horizon添加给dom的属性未能复制 + // 真实 dom 获取的keys只包含新增的属性 + // 比如真实 dom 拿到的 keys 一般只有两个 horizon 自定义属性 + // 但考虑到用户可能自定义其他属性,所以采用遍历赋值的方式 + const customizeKeys = Object.keys(realNode); const keyLength = customizeKeys.length; for(let i = 0; i < keyLength; i++) { const key = customizeKeys[i]; - cloneDom[key] = realNode(key); + // 测试代码 mock 实例的全部可遍历属性都会被Object.keys方法读取到 + // children 属性被复制意味着复制了子节点,因此要排除 + if (key !== 'children') { + cloneDom[key] = realNode[key]; // 复制cloneNode未能复制的属性 + } } const parentObj = findDomParent(vNode); const currentParent = parentObj.parentDom; - const clearChild = vNode.clearChild as VNode; - // 卸载子vNode,递归遍历子vNode - unmountNestedVNodes(clearChild); // node.child 为空,需要添加原有的child + let clearChild = vNode.clearChild as VNode; // 上次渲染的child保存在clearChild属性中 + // 卸载 clearChild 和 它的兄弟节点 + while(clearChild) { + // 卸载子vNode,递归遍历子vNode + unmountNestedVNodes(clearChild); + clearVNode(clearChild); + clearChild = clearChild.next as VNode; + } // 在所有子项都卸载后,删除dom树中的节点 removeChildDom(currentParent, vNode.realNode); - clearVNode(clearChild); currentParent.append(cloneDom); vNode.realNode = cloneDom; FlagUtils.removeFlag(vNode, Clear); From 0f5ee5f274fcd0305c123eed42b25401953fc48e Mon Sep 17 00:00:00 2001 From: * <8> Date: Wed, 26 Jan 2022 10:55:58 +0800 Subject: [PATCH 6/6] Match-id-e0e95bb5c52370f74cb10bee7a7f9c78cd0d390b --- libs/horizon/src/dom/DOMInternalKeys.ts | 2 +- libs/horizon/src/renderer/vnode/VNode.ts | 2 +- libs/horizon/src/renderer/vnode/VNodeFlags.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/horizon/src/dom/DOMInternalKeys.ts b/libs/horizon/src/dom/DOMInternalKeys.ts index 724ae509..dec6fed1 100644 --- a/libs/horizon/src/dom/DOMInternalKeys.ts +++ b/libs/horizon/src/dom/DOMInternalKeys.ts @@ -16,7 +16,7 @@ import { const prefix = '_horizon'; -export const internalKeys = { +const internalKeys = { VNode: `${prefix}VNode`, props: `${prefix}Props`, events: `${prefix}Events`, diff --git a/libs/horizon/src/renderer/vnode/VNode.ts b/libs/horizon/src/renderer/vnode/VNode.ts index 82b4ae1e..e5a848de 100644 --- a/libs/horizon/src/renderer/vnode/VNode.ts +++ b/libs/horizon/src/renderer/vnode/VNode.ts @@ -62,7 +62,7 @@ export class VNode { ForceUpdate?: boolean; Clear?: boolean; } = {}; - clearChild: VNode|null = null; + clearChild: VNode | null = null; // one tree相关属性 isCreated: boolean = true; oldHooks: Array> = []; // 保存上一次执行的hook diff --git a/libs/horizon/src/renderer/vnode/VNodeFlags.ts b/libs/horizon/src/renderer/vnode/VNodeFlags.ts index 449d6baa..f511ac85 100644 --- a/libs/horizon/src/renderer/vnode/VNodeFlags.ts +++ b/libs/horizon/src/renderer/vnode/VNodeFlags.ts @@ -20,8 +20,8 @@ export const ShouldCapture = 'ShouldCapture'; export const ForceUpdate = 'ForceUpdate'; export const Clear = 'Clear'; -const FlagArr = [Addition, Update, Deletion, ResetText, Callback, - DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate, Clear]; +const FlagArr = [Addition, Update, Deletion, Clear, ResetText, Callback, + DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate]; const LifecycleEffectArr = [Update, Callback, Ref, Snapshot];