Match-id-2f02df2d3c1d025e6670522bf52a3cf7b1bad90d

This commit is contained in:
* 2022-01-26 11:10:55 +08:00 committed by *
commit 12b653252f
5 changed files with 59 additions and 8 deletions

View File

@ -1,7 +1,7 @@
import type { VNode } from '../Types'; import type { VNode } from '../Types';
import { FlagUtils } from '../vnode/VNodeFlags'; import { FlagUtils } from '../vnode/VNodeFlags';
import { TYPE_COMMON_ELEMENT, TYPE_FRAGMENT, TYPE_PORTAL } from '../../external/JSXElementType'; 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 {updateVNode, createVNode, createVNodeFromElement, updateVNodePath} from '../vnode/VNodeCreator';
import { import {
isSameType, isSameType,
@ -320,7 +320,12 @@ function diffArrayNodes(
// 3. 新节点已经处理完成 // 3. 新节点已经处理完成
if (leftIdx === rightIdx) { if (leftIdx === rightIdx) {
if (isComparing) { if (isComparing) {
deleteVNodes(parentNode, oldNode, rightEndOldNode); if (firstChild && parentNode.tag === DomComponent && newChildren.length === 0) {
FlagUtils.markClear(parentNode);
parentNode.clearChild = firstChild;
} else {
deleteVNodes(parentNode, oldNode, rightEndOldNode);
}
} }
if (rightNewNode) { if (rightNewNode) {

View File

@ -17,7 +17,7 @@ import {
SuspenseComponent, SuspenseComponent,
MemoComponent, MemoComponent,
} from '../vnode/VNodeTags'; } from '../vnode/VNodeTags';
import { FlagUtils, ResetText } from '../vnode/VNodeFlags'; import { FlagUtils, ResetText, Clear } from '../vnode/VNodeFlags';
import { mergeDefaultProps } from '../render/LazyComponent'; import { mergeDefaultProps } from '../render/LazyComponent';
import { import {
submitDomUpdate, submitDomUpdate,
@ -307,6 +307,42 @@ function unmountDomComponents(vNode: VNode): void {
}); });
} }
function submitClear(vNode: VNode): void {
const realNode = vNode.realNode;
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];
// 测试代码 mock 实例的全部可遍历属性都会被Object.keys方法读取到
// children 属性被复制意味着复制了子节点,因此要排除
if (key !== 'children') {
cloneDom[key] = realNode[key]; // 复制cloneNode未能复制的属性
}
}
const parentObj = findDomParent(vNode);
const currentParent = parentObj.parentDom;
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);
currentParent.append(cloneDom);
vNode.realNode = cloneDom;
FlagUtils.removeFlag(vNode, Clear);
vNode.clearChild = null;
}
function submitDeletion(vNode: VNode): void { function submitDeletion(vNode: VNode): void {
// 遍历所有子节点删除dom节点detach ref 和 调用componentWillUnmount() // 遍历所有子节点删除dom节点detach ref 和 调用componentWillUnmount()
unmountDomComponents(vNode); unmountDomComponents(vNode);
@ -353,6 +389,7 @@ export {
submitResetTextContent, submitResetTextContent,
submitAddition, submitAddition,
submitDeletion, submitDeletion,
submitClear,
submitUpdate, submitUpdate,
callAfterSubmitLifeCycles, callAfterSubmitLifeCycles,
attachRef, attachRef,

View File

@ -9,7 +9,7 @@ import {
attachRef, attachRef,
callAfterSubmitLifeCycles, callAfterSubmitLifeCycles,
callBeforeSubmitLifeCycles, submitDeletion, submitAddition, callBeforeSubmitLifeCycles, submitDeletion, submitAddition,
submitResetTextContent, submitUpdate, detachRef, submitResetTextContent, submitUpdate, detachRef, submitClear,
} from './LifeCycleHandler'; } from './LifeCycleHandler';
import {tryRenderRoot, setProcessing} from '../TreeBuilder'; import {tryRenderRoot, setProcessing} from '../TreeBuilder';
import { import {
@ -121,7 +121,7 @@ function submit(dirtyNodes: Array<VNode>) {
} }
} }
const {Addition, Update, Deletion} = node.flags; const {Addition, Update, Deletion, Clear} = node.flags;
if (Addition && Update) { if (Addition && Update) {
// Addition // Addition
submitAddition(node); submitAddition(node);
@ -138,6 +138,9 @@ function submit(dirtyNodes: Array<VNode>) {
} else if (Deletion) { } else if (Deletion) {
submitDeletion(node); submitDeletion(node);
} }
if (Clear) {
submitClear(node);
}
} }
} catch (error) { } catch (error) {
throwIfTrue(node === null, 'Should be working on an effect.'); throwIfTrue(node === null, 'Should be working on an effect.');

View File

@ -60,8 +60,9 @@ export class VNode {
Interrupted?: boolean; Interrupted?: boolean;
ShouldCapture?: boolean; ShouldCapture?: boolean;
ForceUpdate?: boolean; ForceUpdate?: boolean;
Clear?: boolean;
} = {}; } = {};
clearChild: VNode | null = null;
// one tree相关属性 // one tree相关属性
isCreated: boolean = true; isCreated: boolean = true;
oldHooks: Array<Hook<any, any>> = []; // 保存上一次执行的hook oldHooks: Array<Hook<any, any>> = []; // 保存上一次执行的hook

View File

@ -18,9 +18,10 @@ export const Interrupted = 'Interrupted';
export const ShouldCapture = 'ShouldCapture'; export const ShouldCapture = 'ShouldCapture';
// For suspense // For suspense
export const ForceUpdate = 'ForceUpdate'; export const ForceUpdate = 'ForceUpdate';
export const Clear = 'Clear';
const FlagArr = [Addition, Update, Deletion, ResetText, Callback, const FlagArr = [Addition, Update, Deletion, Clear, ResetText, Callback,
DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate]; DidCapture, Ref, Snapshot, Interrupted, ShouldCapture, ForceUpdate];
const LifecycleEffectArr = [Update, Callback, Ref, Snapshot]; const LifecycleEffectArr = [Update, Callback, Ref, Snapshot];
@ -90,5 +91,9 @@ export class FlagUtils {
static markForceUpdate(node: VNode) { static markForceUpdate(node: VNode) {
node.flags.ForceUpdate = true; node.flags.ForceUpdate = true;
} }
static markClear(node: VNode) {
node.flags.Clear = true;
}
} }