Explorar el Código

refactor(keyedFragment): move key assignment for dynamic fragments from compiler to runtime (#14293)

edison hace 3 meses
padre
commit
7341a05c6d

+ 0 - 4
packages/compiler-vapor/__tests__/transforms/TransformTransition.spec.ts

@@ -47,8 +47,6 @@ describe('compiler: transition', () => {
     )
 
     expect(code).toMatchSnapshot()
-    // n2 should have a key
-    expect(code).contains('n2.$key = 2')
   })
 
   test('work with dynamic keyed children', () => {
@@ -60,8 +58,6 @@ describe('compiler: transition', () => {
 
     expect(code).toMatchSnapshot()
     expect(code).contains('_createKeyedFragment(() => _ctx.key')
-    // should preserve key
-    expect(code).contains('n0.$key = _ctx.key')
   })
 
   function checkWarning(template: string, shouldWarn = true) {

+ 0 - 5
packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap

@@ -43,11 +43,9 @@ export function render(_ctx) {
     "default": () => {
       const n0 = _createIf(() => (_ctx.a), () => {
         const n2 = t0()
-        n2.$key = 2
         return n2
       }, () => _createIf(() => (_ctx.b), () => {
         const n5 = t0()
-        n5.$key = 5
         return n5
       }, () => {
         const n14 = t2()
@@ -59,7 +57,6 @@ export function render(_ctx) {
           const n13 = t1()
           return n13
         })
-        n14.$key = 14
         return n14
       }))
       return [n0, n3, n7]
@@ -99,7 +96,6 @@ export function render(_ctx) {
     "default": () => {
       return _createKeyedFragment(() => _ctx.key, () => {
         const n0 = t0()
-        n0.$key = _ctx.key
         return n0
       })
     }
@@ -117,7 +113,6 @@ export function render(_ctx) {
     "default": () => {
       const n0 = _createIf(() => (_ctx.show), () => {
         const n2 = t0()
-        n2.$key = 2
         return n2
       })
       return n0

+ 1 - 11
packages/compiler-vapor/src/generators/block.ts

@@ -13,7 +13,6 @@ import type { CodegenContext } from '../generate'
 import { genEffects, genOperations } from './operation'
 import { genChildren, genSelf } from './template'
 import { toValidAssetId } from '@vue/compiler-dom'
-import { genExpression } from './expression'
 
 export function genBlock(
   oper: BlockIRNode,
@@ -40,7 +39,7 @@ export function genBlockContent(
   genEffectsExtraFrag?: () => CodeFragment[],
 ): CodeFragment[] {
   const [frag, push] = buildCodeFragment()
-  const { dynamic, effect, operation, returns, key } = block
+  const { dynamic, effect, operation, returns } = block
   const resetBlock = context.enterBlock(block)
 
   if (root) {
@@ -78,15 +77,6 @@ export function genBlockContent(
     push(NEWLINE, `deferredApplyVShows.forEach(fn => fn())`)
   }
 
-  if (dynamic.needsKey) {
-    for (const child of dynamic.children) {
-      const keyValue = key
-        ? genExpression(key, context)
-        : JSON.stringify(child.id)
-      push(NEWLINE, `n${child.id}.$key = `, ...keyValue)
-    }
-  }
-
   push(NEWLINE, `return `)
 
   const returnNodes = returns.map(n => `n${n}`)

+ 0 - 1
packages/compiler-vapor/src/ir/index.ts

@@ -275,7 +275,6 @@ export interface IRDynamicInfo {
   children: IRDynamicInfo[]
   template?: number
   hasDynamicChild?: boolean
-  needsKey?: boolean
   operation?: OperationNode
   ifBranch?: boolean
 }

+ 1 - 4
packages/compiler-vapor/src/transforms/vIf.ts

@@ -18,7 +18,7 @@ import {
 import { extend } from '@vue/shared'
 import { newBlock, wrapTemplate } from './utils'
 import { getSiblingIf } from './transformComment'
-import { isInTransition, isStaticExpression } from '../utils'
+import { isStaticExpression } from '../utils'
 
 export const transformVIf: NodeTransform = createStructuralDirectiveTransform(
   ['if', 'else', 'else-if'],
@@ -138,8 +138,5 @@ export function createIfBranch(
   const branch: BlockIRNode = newBlock(node)
   const exitBlock = context.enterBlock(branch)
   context.reference()
-  // generate key for branch result when it's in transition
-  // the key will be used to track node leaving at runtime
-  branch.dynamic.needsKey = isInTransition(context)
   return [branch, exitBlock]
 }

+ 6 - 10
packages/compiler-vapor/src/transforms/vSlot.ts

@@ -6,6 +6,7 @@ import {
   type SimpleExpressionNode,
   type TemplateChildNode,
   createCompilerError,
+  isCommentOrWhitespace,
   isTemplateNode,
   isVSlot,
 } from '@vue/compiler-dom'
@@ -88,10 +89,12 @@ function transformComponentSlot(
     })
   }
 
-  let slotKey
+  const [block, onExit] = createSlotBlock(node, dir, context)
+
+  // only add key for slot content inside Transition
   if (isTransitionNode(node) && nonSlotTemplateChildren.length) {
     const nonCommentChild = nonSlotTemplateChildren.find(
-      n => n.type !== NodeTypes.COMMENT,
+      n => !isCommentOrWhitespace(n),
     )
     if (nonCommentChild) {
       const keyProp = findProp(
@@ -99,13 +102,11 @@ function transformComponentSlot(
         'key',
       ) as VaporDirectiveNode
       if (keyProp) {
-        slotKey = keyProp.exp
+        block.key = keyProp.exp
       }
     }
   }
 
-  const [block, onExit] = createSlotBlock(node, dir, context, slotKey)
-
   const { slots } = context
 
   return () => {
@@ -265,14 +266,9 @@ function createSlotBlock(
   slotNode: ElementNode,
   dir: VaporDirectiveNode | undefined,
   context: TransformContext<ElementNode>,
-  key: SimpleExpressionNode | undefined = undefined,
 ): [SlotBlockIRNode, () => void] {
   const block: SlotBlockIRNode = newBlock(slotNode)
   block.props = dir && dir.exp
-  if (key) {
-    block.key = key
-    block.dynamic.needsKey = true
-  }
   const exitBlock = context.enterBlock(block)
   return [block, exitBlock]
 }

+ 4 - 1
packages/runtime-vapor/src/apiCreateFragment.ts

@@ -25,7 +25,10 @@ export function createKeyedFragment(key: () => any, render: BlockFn): Block {
   const _isLastInsertion = isLastInsertion
   if (!isHydrating) resetInsertionState()
 
-  const frag = __DEV__ ? new DynamicFragment('keyed') : new DynamicFragment()
+  const frag = __DEV__
+    ? new DynamicFragment('keyed', true)
+    : new DynamicFragment(undefined, true)
+
   renderEffect(() => frag.update(render, key()))
 
   if (!isHydrating) {

+ 25 - 2
packages/runtime-vapor/src/fragment.ts

@@ -19,7 +19,11 @@ import {
   setCurrentInstance,
   warnExtraneousAttributes,
 } from '@vue/runtime-dom'
-import { type VaporComponentInstance, applyFallthroughProps } from './component'
+import {
+  type VaporComponentInstance,
+  applyFallthroughProps,
+  isVaporComponent,
+} from './component'
 import type { NodeRef } from './apiTemplateRef'
 import {
   hydrateDynamicFragment,
@@ -75,6 +79,7 @@ export class DynamicFragment extends VaporFragment {
   pending?: { render?: BlockFn; key: any }
   fallback?: BlockFn
   anchorLabel?: string
+  keyed?: boolean
 
   // fallthrough attrs
   attrs?: Record<string, any>
@@ -95,8 +100,9 @@ export class DynamicFragment extends VaporFragment {
 
   slotOwner: VaporComponentInstance | null
 
-  constructor(anchorLabel?: string) {
+  constructor(anchorLabel?: string, keyed: boolean = false) {
     super([])
+    this.keyed = keyed
     this.slotOwner = currentSlotOwner
     if (isHydrating) {
       this.anchorLabel = anchorLabel
@@ -231,6 +237,9 @@ export class DynamicFragment extends VaporFragment {
       if (prev !== undefined) setCurrentInstance(...prev)
       setCurrentSlotOwner(prevOwner)
 
+      // set key on nodes
+      if (this.keyed) setKey(this.nodes, this.current)
+
       if (transition) {
         this.$transition = applyTransitionHooks(this.nodes, transition)
       }
@@ -324,3 +333,17 @@ export function isDynamicFragment(
 ): val is DynamicFragment {
   return val instanceof DynamicFragment
 }
+
+function setKey(block: Block & { $key?: any }, key: any) {
+  if (block instanceof Node) {
+    block.$key = key
+  } else if (isVaporComponent(block)) {
+    setKey(block.block, key)
+  } else if (isArray(block)) {
+    for (const b of block) {
+      setKey(b, key)
+    }
+  } else {
+    setKey(block.nodes, key)
+  }
+}