]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
perf(vapor): v-for remove all fast path
authorEvan You <evan@vuejs.org>
Mon, 10 Feb 2025 09:37:24 +0000 (17:37 +0800)
committerEvan You <evan@vuejs.org>
Mon, 10 Feb 2025 09:37:24 +0000 (17:37 +0800)
packages/compiler-vapor/src/generators/for.ts
packages/compiler-vapor/src/ir/index.ts
packages/compiler-vapor/src/transforms/vFor.ts
packages/runtime-vapor/src/apiCreateFor.ts

index 12d21b351518706f6124193efb4a8f7527b5375d..6f596499ff103d58fc74441d44befc9ebbbb51db 100644 (file)
@@ -16,8 +16,18 @@ export function genFor(
   context: CodegenContext,
 ): CodeFragment[] {
   const { helper } = context
-  const { source, value, key, index, render, keyProp, once, id, component } =
-    oper
+  const {
+    source,
+    value,
+    key,
+    index,
+    render,
+    keyProp,
+    once,
+    id,
+    component,
+    onlyChild,
+  } = oper
 
   let rawValue: string | null = null
   const rawKey = key && key.content
@@ -80,6 +90,7 @@ export function genFor(
       genCallback(keyProp),
       component && 'true',
       once && 'true',
+      onlyChild && `true`,
       // todo: hydrationNode
     ),
   ]
index cf45a60139fb11019a0b67f22320d6b423b3ba3b..361cafc0c17c59e7a4ebc70cc4636b0c34066242 100644 (file)
@@ -89,6 +89,7 @@ export interface ForIRNode extends BaseIRNode, IRFor {
   render: BlockIRNode
   once: boolean
   component: boolean
+  onlyChild: boolean
 }
 
 export interface SetPropIRNode extends BaseIRNode {
index 7b477882235c1ab94c2dd53f7e38f67097260825..83bda630672af52886762395fe0a38f2977f4a14 100644 (file)
@@ -57,6 +57,16 @@ export function processFor(
 
   return (): void => {
     exitBlock()
+
+    const { parent } = context
+
+    // if v-for is the only child of a parent element, it can go the fast path
+    // when the entire list is emptied
+    const isOnlyChild =
+      parent &&
+      parent.block.node !== parent.node &&
+      parent.node.children.length === 1
+
     context.registerOperation({
       type: IRNodeTypes.FOR,
       id,
@@ -68,6 +78,7 @@ export function processFor(
       render,
       once: context.inVOnce,
       component: isComponent,
+      onlyChild: !!isOnlyChild,
     })
   }
 }
index 1d6d7190116611c011fb4ba5991c8c40a6112603..f8f0988704c76c4a9f4d2332092078084bd4a5ce 100644 (file)
@@ -70,6 +70,7 @@ export const createFor = (
    */
   isComponent = false,
   once?: boolean,
+  canUseFastRemove?: boolean,
   // hydrationNode?: Node,
 ): VaporFragment => {
   let isMounted = false
@@ -105,9 +106,14 @@ export const createFor = (
           mount(source, i)
         }
       } else if (!newLength) {
-        // fast path for clearing
+        // fast path for clearing all
+        const doRemove = !canUseFastRemove
         for (let i = 0; i < oldLength; i++) {
-          unmount(oldBlocks[i])
+          unmount(oldBlocks[i], doRemove)
+        }
+        if (canUseFastRemove) {
+          parent!.textContent = ''
+          parent!.appendChild(parentAnchor)
         }
       } else if (!getKey) {
         // unkeyed fast path
@@ -343,9 +349,9 @@ export const createFor = (
     }
   }
 
-  const unmount = ({ nodes, scope }: ForBlock) => {
+  const unmount = ({ nodes, scope }: ForBlock, doRemove = true) => {
     scope && scope.stop()
-    removeBlock(nodes, parent!)
+    doRemove && removeBlock(nodes, parent!)
   }
 
   once ? renderList() : renderEffect(renderList)