From: daiwei Date: Tue, 15 Jul 2025 01:45:47 +0000 (+0800) Subject: chore: Merge branch 'minor' into edison/feat/vaporTransition X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F12962%2Fhead;p=thirdparty%2Fvuejs%2Fcore.git chore: Merge branch 'minor' into edison/feat/vaporTransition --- 98e1dd1156b6635b9d670411bce93be34ee107fb diff --cc packages/compiler-vapor/src/generators/block.ts index 0811921bd9,3034739475..d101962ba4 --- a/packages/compiler-vapor/src/generators/block.ts +++ b/packages/compiler-vapor/src/generators/block.ts @@@ -38,16 -36,12 +37,16 @@@ export function genBlockContent block: BlockIRNode, context: CodegenContext, root?: boolean, - customReturns?: (returns: CodeFragment[]) => CodeFragment[], + genEffectsExtraFrag?: () => 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 (block.hasDeferredVShow) { + push(NEWLINE, `const deferredApplyVShows = []`) + } + if (root) { for (let name of context.ir.component) { const id = toValidAssetId(name, 'component') @@@ -75,21 -69,8 +74,21 @@@ } push(...genOperations(operation, context)) - push(...genEffects(effect, context)) + push(...genEffects(effect, context, genEffectsExtraFrag)) + if (block.hasDeferredVShow) { + 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}`) diff --cc packages/runtime-core/src/apiCreateApp.ts index 167b2ae28e,a1409a7fe4..4c18a11f49 --- a/packages/runtime-core/src/apiCreateApp.ts +++ b/packages/runtime-core/src/apiCreateApp.ts @@@ -26,8 -26,8 +26,8 @@@ import type { InjectionKey } from './ap import { warn } from './warning' import type { VNode } from './vnode' import { devtoolsInitApp, devtoolsUnmountApp } from './devtools' - import { NO, extend, isFunction, isObject } from '@vue/shared' + import { NO, extend, hasOwn, isFunction, isObject } from '@vue/shared' -import { version } from '.' +import { type TransitionHooks, version } from '.' import { installAppCompatProperties } from './compat/global' import type { NormalizedPropsOptions } from './componentProps' import type { ObjectEmitsOptions } from './componentEmits' diff --cc packages/runtime-core/src/renderer.ts index 87d71d7021,bad40f1439..37e50fb233 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@@ -731,22 -738,25 +738,26 @@@ function baseCreateRenderer } // #1583 For inside suspense + suspense not resolved case, enter hook should call when suspense resolved // #1689 For inside suspense + suspense resolved case, just call it - const needCallTransitionHooks = needTransition(parentSuspense, transition) - if (needCallTransitionHooks) { - transition!.beforeEnter(el) + if (transition) { + performTransitionEnter( + el, + transition, + () => hostInsert(el, container, anchor), + parentSuspense, + ) + } else { + hostInsert(el, container, anchor) } - hostInsert(el, container, anchor) - if ( - (vnodeHook = props && props.onVnodeMounted) || - needCallTransitionHooks || - dirs - ) { + + if ((vnodeHook = props && props.onVnodeMounted) || dirs) { - queuePostRenderEffect(() => { - vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode) - dirs && invokeDirectiveHook(vnode, null, parentComponent, 'mounted') - }, parentSuspense) + queuePostRenderEffect( + () => { + vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode) - needCallTransitionHooks && transition!.enter(el) + dirs && invokeDirectiveHook(vnode, null, parentComponent, 'mounted') + }, + undefined, + parentSuspense, + ) } } @@@ -2657,50 -2744,7 +2732,50 @@@ export function invalidateMount(hooks: } } -function getVaporInterface( +// shared between vdom and vapor +export function performTransitionEnter( + el: RendererElement, + transition: TransitionHooks, + insert: () => void, + parentSuspense: SuspenseBoundary | null, +): void { + if (needTransition(parentSuspense, transition)) { + transition.beforeEnter(el) + insert() - queuePostRenderEffect(() => transition.enter(el), parentSuspense) ++ queuePostRenderEffect(() => transition.enter(el), undefined, parentSuspense) + } else { + insert() + } +} + +// shared between vdom and vapor +export function performTransitionLeave( + el: RendererElement, + transition: TransitionHooks, + remove: () => void, + isElement: boolean = true, +): void { + const performRemove = () => { + remove() + if (transition && !transition.persisted && transition.afterLeave) { + transition.afterLeave() + } + } + + if (isElement && transition && !transition.persisted) { + const { leave, delayLeave } = transition + const performLeave = () => leave(el, performRemove) + if (delayLeave) { + delayLeave(el, performRemove, performLeave) + } else { + performLeave() + } + } else { + performRemove() + } +} + +export function getVaporInterface( instance: ComponentInternalInstance | null, vnode: VNode, ): VaporInteropInterface { diff --cc packages/runtime-vapor/src/apiCreateFor.ts index f60294f29f,426a5c56b5..8b03a59bfc --- a/packages/runtime-vapor/src/apiCreateFor.ts +++ b/packages/runtime-vapor/src/apiCreateFor.ts @@@ -329,14 -363,9 +364,14 @@@ export const createFor = itemRef, keyRef, indexRef, - getKey && getKey(item, key, index), + key2, )) + // apply transition for new nodes + if (frag.$transition) { + applyTransitionHooks(block.nodes, frag.$transition, false) + } + if (parent) insert(block.nodes, parent, anchor) return block diff --cc packages/runtime-vapor/src/block.ts index 6c882badcd,e021ce84b0..09a7ed2e70 --- a/packages/runtime-vapor/src/block.ts +++ b/packages/runtime-vapor/src/block.ts @@@ -6,40 -6,15 +6,40 @@@ import unmountComponent, } from './component' import { createComment, createTextNode } from './dom/node' - import { EffectScope, pauseTracking, resetTracking } from '@vue/reactivity' + import { EffectScope, setActiveSub } from '@vue/reactivity' import { isHydrating } from './dom/hydration' +import { + type TransitionHooks, + type TransitionProps, + type TransitionState, + performTransitionEnter, + performTransitionLeave, +} from '@vue/runtime-dom' +import { + applyTransitionHooks, + applyTransitionLeaveHooks, +} from './components/Transition' + +export interface TransitionOptions { + $key?: any + $transition?: VaporTransitionHooks +} + +export interface VaporTransitionHooks extends TransitionHooks { + state: TransitionState + props: TransitionProps + instance: VaporComponentInstance + // mark transition hooks as disabled so that it skips during + // inserting + disabled?: boolean +} + +export type TransitionBlock = + | (Node & TransitionOptions) + | (VaporFragment & TransitionOptions) + | (DynamicFragment & TransitionOptions) -export type Block = - | Node - | VaporFragment - | DynamicFragment - | VaporComponentInstance - | Block[] +export type Block = TransitionBlock | VaporComponentInstance | Block[] export type BlockFn = (...args: any[]) => Block @@@ -78,40 -47,23 +78,40 @@@ export class DynamicFragment extends Va } this.current = key - pauseTracking() + const prevSub = setActiveSub() const parent = this.anchor.parentNode + const transition = this.$transition + const renderBranch = () => { + if (render) { + this.scope = new EffectScope() + this.nodes = this.scope.run(render) || [] + if (transition) { + this.$transition = applyTransitionHooks(this.nodes, transition) + } + if (parent) insert(this.nodes, parent, this.anchor) + } else { + this.scope = undefined + this.nodes = [] + } + } // teardown previous branch if (this.scope) { this.scope.stop() - parent && remove(this.nodes, parent) + const mode = transition && transition.mode + if (mode) { + applyTransitionLeaveHooks(this.nodes, transition, renderBranch) + parent && remove(this.nodes, parent) + if (mode === 'out-in') { - resetTracking() ++ setActiveSub(prevSub) + return + } + } else { + parent && remove(this.nodes, parent) + } } - if (render) { - this.scope = new EffectScope() - this.nodes = this.scope.run(render) || [] - if (parent) insert(this.nodes, parent, this.anchor) - } else { - this.scope = undefined - this.nodes = [] - } + renderBranch() if (this.fallback && !isValidBlock(this.nodes)) { parent && remove(this.nodes, parent) diff --cc packages/runtime-vapor/src/component.ts index a407268eb5,da57882c49..d3fb8453ae --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@@ -297,21 -289,36 +295,36 @@@ export function applyFallthroughProps */ export function devRender(instance: VaporComponentInstance): void { instance.block = - callWithErrorHandling( - instance.type.render!, - instance, - ErrorCodes.RENDER_FUNCTION, - [ - instance.setupState, - instance.props, - instance.emit, - instance.attrs, - instance.slots, - ], - ) || [] + (instance.type.render + ? callWithErrorHandling( + instance.type.render, + instance, + ErrorCodes.RENDER_FUNCTION, + [ + instance.setupState, + instance.props, + instance.emit, + instance.attrs, + instance.slots, + ], + ) + : callWithErrorHandling( + isFunction(instance.type) ? instance.type : instance.type.setup!, + instance, + ErrorCodes.SETUP_FUNCTION, + [ + instance.props, + { + slots: instance.slots, + attrs: instance.attrs, + emit: instance.emit, + expose: instance.expose, + }, + ], + )) || [] } -const emptyContext: GenericAppContext = { +export const emptyContext: GenericAppContext = { app: null as any, config: {}, provides: /*@__PURE__*/ Object.create(null), diff --cc packages/runtime-vapor/src/vdomInterop.ts index 15e595f3ec,8c1dd2cee2..b5bb141ad3 --- a/packages/runtime-vapor/src/vdomInterop.ts +++ b/packages/runtime-vapor/src/vdomInterop.ts @@@ -17,7 -16,7 +17,8 @@@ import isEmitListener, onScopeDispose, renderSlot, + setTransitionHooks as setVNodeTransitionHooks, + shallowReactive, shallowRef, simpleSetCurrentInstance, } from '@vue/runtime-dom'