Просмотр исходного кода

perf(runtime-vapor): avoid unnecessary dynamic fragment key propagation

Co-authored-by: Copilot <copilot@github.com>
daiwei 1 месяц назад
Родитель
Сommit
735c9e13d7

+ 97 - 0
packages/runtime-vapor/__tests__/if.spec.ts

@@ -1,5 +1,7 @@
 import {
+  VaporTransition,
   child,
+  createComponent,
   createIf,
   insert,
   renderEffect,
@@ -185,6 +187,101 @@ describe('createIf', () => {
     expect(onUpdated).toHaveBeenCalledTimes(2)
   })
 
+  test('should not set branch block key without Transition or KeepAlive', async () => {
+    const show = ref(true)
+    const t0 = template('<div>foo</div>')
+    const t1 = template('<div>bar</div>')
+    let branch!: any
+
+    const { host } = define(() =>
+      createIf(
+        () => show.value,
+        () => (branch = t0()),
+        () => (branch = t1()),
+        undefined,
+        undefined,
+        0,
+      ),
+    ).render()
+
+    expect(host.innerHTML).toBe('<div>foo</div><!--if-->')
+    expect(branch.$key).toBeUndefined()
+
+    show.value = false
+    await nextTick()
+
+    expect(host.innerHTML).toBe('<div>bar</div><!--if-->')
+    expect(branch.$key).toBeUndefined()
+  })
+
+  test('should not set branch block key outside Transition after Transition is used', async () => {
+    const show = ref(true)
+    const transitionChild = template('<span>transition</span>')
+    const t0 = template('<div>foo</div>')
+    const t1 = template('<div>bar</div>')
+    let branch!: any
+
+    const { host } = define(() => [
+      createComponent(
+        VaporTransition,
+        null,
+        {
+          default: () => transitionChild(),
+        },
+        true,
+      ),
+      createIf(
+        () => show.value,
+        () => (branch = t0()),
+        () => (branch = t1()),
+        undefined,
+        undefined,
+        0,
+      ),
+    ]).render()
+
+    expect(host.innerHTML).toBe(
+      '<span>transition</span><div>foo</div><!--if-->',
+    )
+    expect(branch.$key).toBeUndefined()
+
+    show.value = false
+    await nextTick()
+
+    expect(host.innerHTML).toBe(
+      '<span>transition</span><div>bar</div><!--if-->',
+    )
+    expect(branch.$key).toBeUndefined()
+  })
+
+  test('should set branch block key inside Transition', () => {
+    const show = ref(true)
+    const t0 = template('<div>foo</div>')
+    const t1 = template('<div>bar</div>')
+    let branch!: any
+
+    define(() =>
+      createComponent(
+        VaporTransition,
+        null,
+        {
+          default: () =>
+            createIf(
+              () => show.value,
+              () => (branch = t0()),
+              () => (branch = t1()),
+              undefined,
+              undefined,
+              0,
+            ),
+        },
+        true,
+      ),
+    ).render()
+
+    expect(branch.$key).toBe('00')
+  })
+
   // vapor custom directives have no lifecycle hooks.
   test.todo('should work with directive hooks', async () => {
     const calls: string[] = []

+ 1 - 5
packages/runtime-vapor/src/component.ts

@@ -130,7 +130,7 @@ import {
 } from './suspense'
 import { isInteropEnabled } from './vdomInteropState'
 import { setComponentScopeId, setScopeId } from './scopeId'
-import { isTransitionEnabled } from './transition'
+import { isTransitionEnabled, isVaporTransition } from './transition'
 
 export { currentInstance } from '@vue/runtime-dom'
 
@@ -1155,10 +1155,6 @@ export function getRootElement(
   }
 }
 
-function isVaporTransition(component: VaporComponent): boolean {
-  return getComponentName(component) === 'VaporTransition'
-}
-
 function handleSetupResult(
   setupResult: any,
   component: VaporComponent,

+ 1 - 2
packages/runtime-vapor/src/components/Transition.ts

@@ -29,7 +29,7 @@ import type {
   TransitionOptions,
   VaporTransitionHooks,
 } from '../block'
-import { registerTransitionHooks } from '../transition'
+import { displayName, registerTransitionHooks } from '../transition'
 import {
   type FunctionalVaporComponent,
   type VaporComponentInstance,
@@ -51,7 +51,6 @@ import {
 import { type PendingVShow, setCurrentPendingVShows } from '../directives/vShow'
 import { isInteropEnabled } from '../vdomInteropState'
 
-const displayName = 'VaporTransition'
 export type ResolvedTransitionBlock = (
   | Element
   | VaporFragment

+ 16 - 1
packages/runtime-vapor/src/fragment.ts

@@ -58,6 +58,7 @@ import {
   applyTransitionHooks,
   applyTransitionLeaveHooks,
   isTransitionEnabled,
+  isVaporTransition,
 } from './transition'
 
 export class VaporFragment<
@@ -170,6 +171,7 @@ export class DynamicFragment extends VaporFragment {
   pending?: { render?: BlockFn; key: any }
   anchorLabel?: string
   keyed?: boolean
+  inTransition?: boolean
 
   // fallthrough attrs
   attrs?: Record<string, any>
@@ -180,6 +182,13 @@ export class DynamicFragment extends VaporFragment {
   ) {
     super([])
     this.keyed = keyed
+    if (
+      isTransitionEnabled &&
+      currentInstance &&
+      isVaporTransition(currentInstance.type)
+    ) {
+      this.inTransition = true
+    }
     if (isHydrating) {
       this.anchorLabel = anchorLabel
       if (locate) locateHydrationNode()
@@ -339,7 +348,13 @@ export class DynamicFragment extends VaporFragment {
         } finally {
           // propagate the fragment key onto freshly rendered nodes.
           const key = this.keyed ? this.current : this.$key
-          if (key !== undefined) setBlockKey(this.nodes, key)
+          // Only propagate branch keys when Transition or KeepAlive consumes them.
+          if (
+            key !== undefined &&
+            (transition || this.inTransition || keepAliveCtx)
+          ) {
+            setBlockKey(this.nodes, key)
+          }
 
           if (isTransitionEnabled && transition) {
             this.$transition = applyTransitionHooks(this.nodes, transition)

+ 8 - 0
packages/runtime-vapor/src/transition.ts

@@ -1,5 +1,7 @@
+import { getComponentName } from '@vue/runtime-dom'
 import type { Block } from './block'
 import type { VaporTransitionHooks } from './block'
+import type { VaporComponent } from './component'
 
 // Transition hooks registry for tree-shaking
 // These are registered by Transition component when it's used
@@ -26,3 +28,9 @@ export function registerTransitionHooks(
   applyTransitionHooks = applyHooks
   applyTransitionLeaveHooks = applyLeaveHooks
 }
+
+export const displayName = 'VaporTransition'
+
+export function isVaporTransition(component: VaporComponent): boolean {
+  return getComponentName(component) === displayName
+}