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

refactor(vapor): use bitwise flags for v-for runtime optimizations

Evan You преди 1 година
родител
ревизия
242cc15fa6

+ 1 - 1
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap

@@ -23,7 +23,7 @@ export function render(_ctx) {
     const n2 = t0()
     _setTemplateRef(n2, "foo", void 0, true)
     return n2
-  }, null, null, true)
+  }, null, 4)
   return n0
 }"
 `;

+ 1 - 1
packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap

@@ -77,7 +77,7 @@ export function render(_ctx) {
       const x4 = _child(n4)
       _renderEffect(() => _setText(x4, _toDisplayString(_for_item1.value+_for_item0.value)))
       return n4
-    }, null, null, null, true)
+    }, null, 1)
     _insert(n2, n5)
     return n5
   })

+ 1 - 1
packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap

@@ -68,7 +68,7 @@ export function render(_ctx) {
   const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
     const n2 = t0()
     return n2
-  }, null, null, true)
+  }, null, 4)
   return n0
 }"
 `;

+ 13 - 3
packages/compiler-vapor/src/generators/for.ts

@@ -10,6 +10,7 @@ import type { ForIRNode } from '../ir'
 import { type CodeFragment, NEWLINE, genCall, genMulti } from './utils'
 import type { Identifier } from '@babel/types'
 import { parseExpression } from '@babel/parser'
+import { VaporVForFlags } from '../../../shared/src/vaporFlags'
 
 export function genFor(
   oper: ForIRNode,
@@ -80,6 +81,17 @@ export function genFor(
   const blockFn = context.withId(() => genBlock(render, context, args), idMap)
   exitScope()
 
+  let flags = 0
+  if (onlyChild) {
+    flags |= VaporVForFlags.FAST_REMOVE
+  }
+  if (component) {
+    flags |= VaporVForFlags.IS_COMPONENT
+  }
+  if (once) {
+    flags |= VaporVForFlags.ONCE
+  }
+
   return [
     NEWLINE,
     `const n${id} = `,
@@ -88,9 +100,7 @@ export function genFor(
       sourceExpr,
       blockFn,
       genCallback(keyProp),
-      component && 'true',
-      once && 'true',
-      onlyChild && `true`,
+      flags ? String(flags) : undefined,
       // todo: hydrationNode
     ),
   ]

+ 9 - 8
packages/runtime-vapor/src/apiCreateFor.ts

@@ -21,6 +21,7 @@ import { warn } from '@vue/runtime-dom'
 import { currentInstance, isVaporComponent } from './component'
 import type { DynamicSlot } from './componentSlots'
 import { renderEffect } from './renderEffect'
+import { VaporVForFlags } from '../../shared/src/vaporFlags'
 
 class ForBlock extends VaporFragment {
   scope: EffectScope | undefined
@@ -64,13 +65,7 @@ export const createFor = (
     index: ShallowRef<number | undefined>,
   ) => Block,
   getKey?: (item: any, key: any, index?: number) => any,
-  /**
-   * Whether this v-for is used directly on a component. If true, we can avoid
-   * creating an extra fragment / scope for each block
-   */
-  isComponent = false,
-  once?: boolean,
-  canUseFastRemove?: boolean,
+  flags = 0,
   // hydrationNode?: Node,
 ): VaporFragment => {
   let isMounted = false
@@ -80,6 +75,8 @@ export const createFor = (
   const parentAnchor = __DEV__ ? createComment('for') : createTextNode()
   const frag = new VaporFragment(oldBlocks)
   const instance = currentInstance!
+  const canUseFastRemove = flags & VaporVForFlags.FAST_REMOVE
+  const isComponent = flags & VaporVForFlags.IS_COMPONENT
 
   if (__DEV__ && !instance) {
     warn('createFor() can only be used inside setup()')
@@ -354,7 +351,11 @@ export const createFor = (
     doRemove && removeBlock(nodes, parent!)
   }
 
-  once ? renderList() : renderEffect(renderList)
+  if (flags & VaporVForFlags.ONCE) {
+    renderList()
+  } else {
+    renderEffect(renderList)
+  }
   return frag
 }
 

+ 20 - 0
packages/shared/src/vaporFlags.ts

@@ -0,0 +1,20 @@
+/**
+ * Flags to optimize vapor `createFor` runtime behavior, shared between the
+ * compiler and the runtime
+ */
+export enum VaporVForFlags {
+  /**
+   * v-for is the only child of a parent container, so it can take the fast
+   * path with textContent = '' when the whole list is emptied
+   */
+  FAST_REMOVE = 1,
+  /**
+   * v-for used on component - we can skip creating child scopes for each block
+   * because the component itself already has a scope.
+   */
+  IS_COMPONENT = 1 << 1,
+  /**
+   * v-for inside v-ince
+   */
+  ONCE = 1 << 2,
+}