Преглед на файлове

wip(vapor): optimize unmounted children removal

Evan You преди 1 година
родител
ревизия
bcb9209c4c
променени са 2 файла, в които са добавени 59 реда и са изтрити 7 реда
  1. 18 0
      packages/runtime-vapor/src/block.ts
  2. 41 7
      packages/runtime-vapor/src/component.ts

+ 18 - 0
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<VaporComponentInstance> | 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
+  }
 }

+ 41 - 7
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)
   }
 }