From: Evan You Date: Sat, 14 Dec 2024 08:28:05 +0000 (+0800) Subject: wip(vapor): optimize unmounted children removal X-Git-Tag: v3.6.0-alpha.1~16^2~147 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bcb9209c4c19e656ca30cb5adbdd34c4532f07c8;p=thirdparty%2Fvuejs%2Fcore.git wip(vapor): optimize unmounted children removal --- diff --git a/packages/runtime-vapor/src/block.ts b/packages/runtime-vapor/src/block.ts index 3951be239b..65b7c70de7 100644 --- a/packages/runtime-vapor/src/block.ts +++ b/packages/runtime-vapor/src/block.ts @@ -126,7 +126,19 @@ export function prepend(parent: ParentNode, ...blocks: Block[]): void { while (i--) insert(blocks[i], parent, 0) } +/** + * Optimized children removal: record all parents with unmounted children + * during each root remove call, and update their children list by filtering + * unmounted children + */ +export let parentsWithUnmountedChildren: Set | null = + null + export function remove(block: Block, parent: ParentNode): void { + const isRoot = !parentsWithUnmountedChildren + if (isRoot) { + parentsWithUnmountedChildren = new Set() + } if (block instanceof Node) { parent.removeChild(block) } else if (isVaporComponent(block)) { @@ -143,4 +155,10 @@ export function remove(block: Block, parent: ParentNode): void { ;(block as DynamicFragment).scope!.stop() } } + if (isRoot) { + for (const i of parentsWithUnmountedChildren!) { + i.children = i.children.filter(n => !n.isUnmounted) + } + parentsWithUnmountedChildren = null + } } diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 2a7ffb0092..61476a098c 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -25,7 +25,13 @@ import { unregisterHMR, warn, } from '@vue/runtime-dom' -import { type Block, insert, isBlock, remove } from './block' +import { + type Block, + insert, + isBlock, + parentsWithUnmountedChildren, + remove, +} from './block' import { markRaw, pauseTracking, @@ -33,7 +39,14 @@ import { resetTracking, unref, } from '@vue/reactivity' -import { EMPTY_OBJ, invokeArrayFns, isFunction, isString } from '@vue/shared' +import { + EMPTY_ARR, + EMPTY_OBJ, + invokeArrayFns, + isFunction, + isString, + remove as removeItem, +} from '@vue/shared' import { type DynamicPropsSource, type RawProps, @@ -465,24 +478,45 @@ export function mountComponent( export function unmountComponent( instance: VaporComponentInstance, - parent?: ParentNode, + parentNode?: ParentNode, ): void { if (instance.isMounted && !instance.isUnmounted) { if (__DEV__ && instance.type.__hmrId) { unregisterHMR(instance) } - if (instance.bum) invokeArrayFns(instance.bum) + if (instance.bum) { + invokeArrayFns(instance.bum) + } + instance.scope.stop() + for (const c of instance.children) { unmountComponent(c) } - if (parent) remove(instance.block, parent) + instance.children = EMPTY_ARR as any + + if (parentNode) { + // root remove: need to both remove this instance's DOM nodes + // and also remove it from the parent's children list. + remove(instance.block, parentNode) + const parentInstance = instance.parent + if (isVaporComponent(parentInstance)) { + if (parentsWithUnmountedChildren) { + // for optimize children removal + parentsWithUnmountedChildren.add(parentInstance) + } else { + removeItem(parentInstance.children, instance) + } + instance.parent = null + } + } + if (instance.um) { queuePostFlushCb(() => invokeArrayFns(instance.um!)) } instance.isUnmounted = true - } else if (parent) { - remove(instance.block, parent) + } else if (parentNode) { + remove(instance.block, parentNode) } }