From: daiwei Date: Tue, 4 Mar 2025 09:55:13 +0000 (+0800) Subject: wip: handle keyed element transition X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=11bcb21204f05aa3521f65d85190393a6e7f50db;p=thirdparty%2Fvuejs%2Fcore.git wip: handle keyed element transition --- diff --git a/packages/compiler-vapor/src/generators/block.ts b/packages/compiler-vapor/src/generators/block.ts index 4e568a91e7..39354e252a 100644 --- a/packages/compiler-vapor/src/generators/block.ts +++ b/packages/compiler-vapor/src/generators/block.ts @@ -13,6 +13,7 @@ import type { CodegenContext } from '../generate' import { genEffects, genOperations } from './operation' import { genChildren } from './template' import { toValidAssetId } from '@vue/compiler-dom' +import { genExpression } from './expression' export function genBlock( oper: BlockIRNode, @@ -40,7 +41,7 @@ export function genBlockContent( customReturns?: (returns: CodeFragment[]) => CodeFragment[], ): CodeFragment[] { const [frag, push] = buildCodeFragment() - const { dynamic, effect, operation, returns } = block + const { dynamic, effect, operation, returns, key } = block const resetBlock = context.enterBlock(block) if (root) { @@ -57,7 +58,10 @@ export function genBlockContent( if (dynamic.needsKey) { for (const child of dynamic.children) { - push(NEWLINE, `n${child.id}.key = ${JSON.stringify(child.id)}`) + const keyValue = key + ? genExpression(key, context) + : JSON.stringify(child.id) + push(NEWLINE, `n${child.id}.key = `, ...keyValue) } } diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index 8b0ab73e78..c0ac494641 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -99,7 +99,7 @@ export function genCreateComponent( return `_${builtInTag}` } return genExpression( - extend(createSimpleExpression(operation.tag, false), { ast: null }), + extend(createSimpleExpression(tag, false), { ast: null }), context, ) } @@ -402,7 +402,7 @@ function genSlotBlockWithProps(oper: SlotBlockIRNode, context: CodegenContext) { let propsName: string | undefined let exitScope: (() => void) | undefined let depth: number | undefined - const { props } = oper + const { props, key } = oper const idsOfProps = new Set() if (props) { @@ -430,11 +430,28 @@ function genSlotBlockWithProps(oper: SlotBlockIRNode, context: CodegenContext) { ? `${propsName}[${JSON.stringify(id)}]` : null), ) - const blockFn = context.withId( + let blockFn = context.withId( () => genBlock(oper, context, [propsName]), idMap, ) exitScope && exitScope() + if (key) { + blockFn = [ + `() => {`, + INDENT_START, + NEWLINE, + `return `, + ...genCall( + context.helper('createKeyedFragment'), + [`() => `, ...genExpression(key, context)], + blockFn, + ), + INDENT_END, + NEWLINE, + `}`, + ] + } + return blockFn } diff --git a/packages/compiler-vapor/src/ir/index.ts b/packages/compiler-vapor/src/ir/index.ts index b048e40d79..71e896e13f 100644 --- a/packages/compiler-vapor/src/ir/index.ts +++ b/packages/compiler-vapor/src/ir/index.ts @@ -40,6 +40,7 @@ export enum IRNodeTypes { export interface BaseIRNode { type: IRNodeTypes + key?: SimpleExpressionNode | undefined } export type CoreHelper = keyof typeof import('packages/runtime-dom/src') diff --git a/packages/compiler-vapor/src/transforms/vIf.ts b/packages/compiler-vapor/src/transforms/vIf.ts index bf9b178fd3..5306cf7057 100644 --- a/packages/compiler-vapor/src/transforms/vIf.ts +++ b/packages/compiler-vapor/src/transforms/vIf.ts @@ -130,11 +130,16 @@ export function createIfBranch( return [branch, exitBlock] } -function isInTransition(context: TransformContext): boolean { +export function isInTransition( + context: TransformContext, +): boolean { const parentNode = context.parent && context.parent.node - return !!( - parentNode && - parentNode.type === NodeTypes.ELEMENT && - (parentNode.tag === 'transition' || parentNode.tag === 'Transition') + return !!(parentNode && isTransitionNode(parentNode as ElementNode)) +} + +export function isTransitionNode(node: ElementNode): boolean { + return ( + node.type === NodeTypes.ELEMENT && + (node.tag === 'transition' || node.tag === 'Transition') ) } diff --git a/packages/compiler-vapor/src/transforms/vSlot.ts b/packages/compiler-vapor/src/transforms/vSlot.ts index d1bf1c6b05..c1b82e2bc5 100644 --- a/packages/compiler-vapor/src/transforms/vSlot.ts +++ b/packages/compiler-vapor/src/transforms/vSlot.ts @@ -23,7 +23,8 @@ import { type SlotBlockIRNode, type VaporDirectiveNode, } from '../ir' -import { findDir, resolveExpression } from '../utils' +import { findDir, findProp, resolveExpression } from '../utils' +import { isTransitionNode } from './vIf' export const transformVSlot: NodeTransform = (node, context) => { if (node.type !== NodeTypes.ELEMENT) return @@ -72,7 +73,18 @@ function transformComponentSlot( !(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot)), ) - const [block, onExit] = createSlotBlock(node, dir, context) + let slotKey + if (isTransitionNode(node)) { + const keyProp = findProp( + nonSlotTemplateChildren[0] as ElementNode, + 'key', + ) as VaporDirectiveNode + if (keyProp) { + slotKey = keyProp.exp + } + } + + const [block, onExit] = createSlotBlock(node, dir, context, slotKey) const { slots } = context @@ -233,9 +245,12 @@ function createSlotBlock( slotNode: ElementNode, dir: VaporDirectiveNode | undefined, context: TransformContext, + key: SimpleExpressionNode | undefined = undefined, ): [SlotBlockIRNode, () => void] { const block: SlotBlockIRNode = newBlock(slotNode) block.props = dir && dir.exp + block.key = key + if (key) block.dynamic.needsKey = true const exitBlock = context.enterBlock(block) return [block, exitBlock] } diff --git a/packages/runtime-vapor/src/apiCreateFragment.ts b/packages/runtime-vapor/src/apiCreateFragment.ts new file mode 100644 index 0000000000..50179b89ef --- /dev/null +++ b/packages/runtime-vapor/src/apiCreateFragment.ts @@ -0,0 +1,10 @@ +import { type Block, type BlockFn, DynamicFragment } from './block' +import { renderEffect } from './renderEffect' + +export function createKeyedFragment(key: () => any, render: BlockFn): Block { + const frag = __DEV__ ? new DynamicFragment('keyed') : new DynamicFragment() + renderEffect(() => { + frag.update(render, key()) + }) + return frag +} diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index 40a847ba8f..c356727f86 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@ -24,6 +24,7 @@ export { } from './dom/prop' export { on, delegate, delegateEvents, setDynamicEvents } from './dom/event' export { createIf } from './apiCreateIf' +export { createKeyedFragment } from './apiCreateFragment' export { createFor, createForSlots,