]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
chore: Merge branch 'vapor' into edison/feat/vaporTransition
authordaiwei <daiwei521@126.com>
Fri, 20 Jun 2025 02:55:50 +0000 (10:55 +0800)
committerdaiwei <daiwei521@126.com>
Fri, 20 Jun 2025 02:55:50 +0000 (10:55 +0800)
21 files changed:
1  2 
packages/compiler-vapor/__tests__/transforms/__snapshots__/TransformTransition.spec.ts.snap
packages/compiler-vapor/src/compile.ts
packages/compiler-vapor/src/generators/block.ts
packages/compiler-vapor/src/generators/component.ts
packages/compiler-vapor/src/ir/index.ts
packages/compiler-vapor/src/transforms/transformElement.ts
packages/compiler-vapor/src/transforms/utils.ts
packages/compiler-vapor/src/transforms/vIf.ts
packages/compiler-vapor/src/transforms/vSlot.ts
packages/runtime-core/src/apiCreateApp.ts
packages/runtime-core/src/components/BaseTransition.ts
packages/runtime-core/src/index.ts
packages/runtime-core/src/renderer.ts
packages/runtime-dom/src/components/TransitionGroup.ts
packages/runtime-vapor/src/apiCreateDynamicComponent.ts
packages/runtime-vapor/src/apiCreateFor.ts
packages/runtime-vapor/src/component.ts
packages/runtime-vapor/src/dom/prop.ts
packages/runtime-vapor/src/dom/template.ts
packages/runtime-vapor/src/index.ts
packages/runtime-vapor/src/vdomInterop.ts

index 2cb5c36e41ec1467ec2aafb99c64430e824bf1e7,0000000000000000000000000000000000000000..8a83143b9b720b37d326245d05c49b6779faab40
mode 100644,000000..100644
--- /dev/null
@@@ -1,128 -1,0 +1,128 @@@
- "import { VaporTransition as _VaporTransition, createIf as _createIf, prepend as _prepend, createComponent as _createComponent, template as _template } from 'vue';
 +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 +
 +exports[`compiler: transition > basic 1`] = `
 +"import { VaporTransition as _VaporTransition, applyVShow as _applyVShow, createComponent as _createComponent, template as _template } from 'vue';
 +const t0 = _template("<h1>foo</h1>")
 +
 +export function render(_ctx) {
 +  const n1 = _createComponent(_VaporTransition, { persisted: () => ("") }, {
 +    "default": () => {
 +      const n0 = t0()
 +      _applyVShow(n0, () => (_ctx.show))
 +      return n0
 +    }
 +  }, true)
 +  return n1
 +}"
 +`;
 +
 +exports[`compiler: transition > inject persisted when child has v-show 1`] = `
 +"import { VaporTransition as _VaporTransition, applyVShow as _applyVShow, createComponent as _createComponent, template as _template } from 'vue';
 +const t0 = _template("<div></div>")
 +
 +export function render(_ctx) {
 +  const n1 = _createComponent(_VaporTransition, { persisted: () => ("") }, {
 +    "default": () => {
 +      const n0 = t0()
 +      _applyVShow(n0, () => (_ctx.ok))
 +      return n0
 +    }
 +  }, true)
 +  return n1
 +}"
 +`;
 +
 +exports[`compiler: transition > the v-if/else-if/else branches in Transition should ignore comments 1`] = `
-         _prepend(n14, n9)
++"import { VaporTransition as _VaporTransition, setInsertionState as _setInsertionState, createIf as _createIf, createComponent as _createComponent, template as _template } from 'vue';
 +const t0 = _template("<div>hey</div>")
 +const t1 = _template("<p></p>")
 +const t2 = _template("<div></div>")
 +
 +export function render(_ctx) {
 +  const n16 = _createComponent(_VaporTransition, null, {
 +    "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()
++        _setInsertionState(n14, 0)
 +        const n9 = _createIf(() => (_ctx.c), () => {
 +          const n11 = t1()
 +          return n11
 +        }, () => {
 +          const n13 = t1()
 +          return n13
 +        })
 +        n14.$key = 14
 +        return n14
 +      }))
 +      return [n0, n3, n7]
 +    }
 +  }, true)
 +  return n16
 +}"
 +`;
 +
 +exports[`compiler: transition > v-show + appear 1`] = `
 +"import { VaporTransition as _VaporTransition, applyVShow as _applyVShow, createComponent as _createComponent, template as _template } from 'vue';
 +const t0 = _template("<h1>foo</h1>")
 +
 +export function render(_ctx) {
 +  const deferredApplyVShows = []
 +  const n1 = _createComponent(_VaporTransition, {
 +    appear: () => (""), 
 +    persisted: () => ("")
 +  }, {
 +    "default": () => {
 +      const n0 = t0()
 +      deferredApplyVShows.push(() => _applyVShow(n0, () => (_ctx.show)))
 +      return n0
 +    }
 +  }, true)
 +  deferredApplyVShows.forEach(fn => fn())
 +  return n1
 +}"
 +`;
 +
 +exports[`compiler: transition > work with dynamic keyed children 1`] = `
 +"import { VaporTransition as _VaporTransition, createKeyedFragment as _createKeyedFragment, createComponent as _createComponent, template as _template } from 'vue';
 +const t0 = _template("<h1>foo</h1>")
 +
 +export function render(_ctx) {
 +  const n1 = _createComponent(_VaporTransition, null, {
 +    "default": () => {
 +      return _createKeyedFragment(() => _ctx.key, () => {
 +        const n0 = t0()
 +        n0.$key = _ctx.key
 +        return n0
 +      })
 +    }
 +  }, true)
 +  return n1
 +}"
 +`;
 +
 +exports[`compiler: transition > work with v-if 1`] = `
 +"import { VaporTransition as _VaporTransition, createIf as _createIf, createComponent as _createComponent, template as _template } from 'vue';
 +const t0 = _template("<h1>foo</h1>")
 +
 +export function render(_ctx) {
 +  const n3 = _createComponent(_VaporTransition, null, {
 +    "default": () => {
 +      const n0 = _createIf(() => (_ctx.show), () => {
 +        const n2 = t0()
 +        n2.$key = 2
 +        return n2
 +      })
 +      return n0
 +    }
 +  }, true)
 +  return n3
 +}"
 +`;
index 944a5ee3af9e756647fca97413ed278c1d8f15bd,ff240dd6eac05068f63d88df62be4e39d97ff13d..0811921bd9655f2f9e61a913ce0495a80c312393
@@@ -11,9 -11,8 +11,9 @@@ import 
  } from './utils'
  import type { CodegenContext } from '../generate'
  import { genEffects, genOperations } from './operation'
- import { genChildren } from './template'
+ import { genChildren, genSelf } from './template'
  import { toValidAssetId } from '@vue/compiler-dom'
 +import { genExpression } from './expression'
  
  export function genBlock(
    oper: BlockIRNode,
@@@ -41,15 -40,25 +41,29 @@@ 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 (block.hasDeferredVShow) {
 +    push(NEWLINE, `const deferredApplyVShows = []`)
 +  }
 +
    if (root) {
-     genResolveAssets('component', 'resolveComponent')
+     for (let name of context.ir.component) {
+       const id = toValidAssetId(name, 'component')
+       const maybeSelfReference = name.endsWith('__self')
+       if (maybeSelfReference) name = name.slice(0, -6)
+       push(
+         NEWLINE,
+         `const ${id} = `,
+         ...genCall(
+           context.helper('resolveComponent'),
+           JSON.stringify(name),
+           // pass additional `maybeSelfReference` flag
+           maybeSelfReference ? 'true' : undefined,
+         ),
+       )
+     }
      genResolveAssets('directive', 'resolveDirective')
    }
  
index 08d0730a564c81a68be2728ce85efac308ff7b11,10705a2c79595733a246d47e9bb7aaf6d80fc33f..70fc0c10a632ac6a602d75d4eb8652ddc0d43fdb
@@@ -53,8 -51,9 +53,9 @@@ export function genCreateComponent
    const rawSlots = genRawSlots(slots, context)
    const [ids, handlers] = processInlineHandlers(props, context)
    const rawProps = context.withId(() => genRawProps(props, context), ids)
    const inlineHandlers: CodeFragment[] = handlers.reduce<CodeFragment[]>(
 -    (acc, { name, value }) => {
 +    (acc, { name, value }: InlineHandler) => {
        const handler = genEventHandler(context, value, undefined, false)
        return [...acc, `const ${name} = `, ...handler, NEWLINE]
      },
index b4126863f11cfc443fa3c008a899f3cd164c5f24,18f0139ab568e4f082c88241bca7f6a87b53bb98..a8130be3890982310973671630a58fd5ef986717
@@@ -54,9 -52,7 +53,8 @@@ export interface BlockIRNode extends Ba
    tempId: number
    effect: IREffect[]
    operation: OperationNode[]
-   expressions: SimpleExpressionNode[]
    returns: number[]
 +  hasDeferredVShow: boolean
  }
  
  export interface RootIRNode {
@@@ -264,7 -259,7 +262,8 @@@ export interface IRDynamicInfo 
    children: IRDynamicInfo[]
    template?: number
    hasDynamicChild?: boolean
 +  needsKey?: boolean
+   operation?: OperationNode
  }
  
  export interface IREffect {
index a42f47eb37d6b3514c183a4427bb47cacb6206d7,f7d0594fe58151ac73eae39833d1978343d7116b..b746999a18a525db223388f96cc1dcf25ea83765
@@@ -29,9 -29,7 +29,8 @@@ export const newBlock = (node: BlockIRN
    effect: [],
    operation: [],
    returns: [],
-   expressions: [],
    tempId: 0,
 +  hasDeferredVShow: false,
  })
  
  export function wrapTemplate(node: ElementNode, dirs: string[]): TemplateNode {
index 9a65e6f1bb4730d7402c5a30f3038940d55bdf3d,3e78913a23ef2fdd40c13a8fcb92a78e43225cb5..05aac4aee3c50cdfb86e693356d81d9f9bfa56eb
@@@ -23,12 -23,8 +23,13 @@@ import 
    type SlotBlockIRNode,
    type VaporDirectiveNode,
  } from '../ir'
 -import { findDir, resolveExpression } from '../utils'
 +import {
 +  findDir,
 +  findProp,
 +  isTransitionNode,
 +  resolveExpression,
 +} from '../utils'
+ import { markNonTemplate } from './transformText'
  
  export const transformVSlot: NodeTransform = (node, context) => {
    if (node.type !== NodeTypes.ELEMENT) return
@@@ -71,24 -67,23 +72,34 @@@ function transformComponentSlot
  ) {
    const { children } = node
    const arg = dir && dir.arg
-   const nonSlotTemplateChildren = children.filter(
-     n =>
-       isNonWhitespaceContent(node) &&
-       !(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot)),
-   )
+   // whitespace: 'preserve'
+   const emptyTextNodes: TemplateChildNode[] = []
+   const nonSlotTemplateChildren = children.filter(n => {
+     if (isNonWhitespaceContent(n)) {
+       return !(n.type === NodeTypes.ELEMENT && n.props.some(isVSlot))
+     } else {
+       emptyTextNodes.push(n)
+     }
+   })
+   if (!nonSlotTemplateChildren.length) {
+     emptyTextNodes.forEach(n => {
+       markNonTemplate(n, context)
+     })
+   }
  
 -  const [block, onExit] = createSlotBlock(node, dir, context)
 +  let slotKey
-   if (isTransitionNode(node)) {
++  if (isTransitionNode(node) && nonSlotTemplateChildren.length) {
 +    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
  
index 4eddabcff270e5a679344988d37e011224dc022f,1ed6f21df7769a695a0643a6c27c38eaeaf30eef..ba0cc93ca52c33e50dc81493bf7ada6dc22aafe1
@@@ -562,7 -557,7 +562,11 @@@ export { startMeasure, endMeasure } fro
   * @internal
   */
  export { initFeatureFlags } from './featureFlags'
 +/**
 + * @internal
 + */
 +export { performTransitionEnter, performTransitionLeave } from './renderer'
+ /**
+  * @internal
+  */
+ export { createInternalObject } from './internalObject'
index 744338b3f45a23e630bae94dc94c03726c02b924,3d02c65c16bc87a3a1919de8543b571ea2a460ae..87d71d702143ce7c3814d81a873dcbcd5aa72261
@@@ -2116,15 -2115,18 +2116,21 @@@ function baseCreateRenderer
        transition
      if (needTransition) {
        if (moveType === MoveType.ENTER) {
 -        transition!.beforeEnter(el!)
 -        hostInsert(el!, container, anchor)
 -        queuePostRenderEffect(() => transition!.enter(el!), parentSuspense)
 +        performTransitionEnter(
 +          el!,
 +          transition,
 +          () => hostInsert(el!, container, anchor),
 +          parentSuspense,
 +        )
        } else {
          const { leave, delayLeave, afterLeave } = transition!
-         const remove = () => hostInsert(el!, container, anchor)
+         const remove = () => {
+           if (vnode.ctx!.isUnmounted) {
+             hostRemove(el!)
+           } else {
+             hostInsert(el!, container, anchor)
+           }
+         }
          const performLeave = () => {
            leave(el!, () => {
              remove()
index 8e38dbf0f232b867303e3f8559ec9fdf4d673c30,72af535d385e0a837df6fccea72e9f0bae6e7213..abf3e0954243dec714c336f73c3801f35af57543
@@@ -96,8 -97,22 +97,9 @@@ const TransitionGroupImpl: ComponentOpt
  
        movedChildren.forEach(c => {
          const el = c.el as ElementWithTransition
 -        const style = el.style
 -        addTransitionClass(el, moveClass)
 -        style.transform = style.webkitTransform = style.transitionDuration = ''
 -        const cb = ((el as any)[moveCbKey] = (e: TransitionEvent) => {
 -          if (e && e.target !== el) {
 -            return
 -          }
 -          if (!e || /transform$/.test(e.propertyName)) {
 -            el.removeEventListener('transitionend', cb)
 -            ;(el as any)[moveCbKey] = null
 -            removeTransitionClass(el, moveClass)
 -          }
 -        })
 -        el.addEventListener('transitionend', cb)
 +        handleMovedChildren(el, moveClass)
        })
+       prevChildren = []
      })
  
      return () => {
index c061c8224cdf27e8c610882d4243956d2af36f7b,945e0f38d8729b9f97fb2ef42296d8dcf62b453f..8a127c2daf196ce32dc8fa386b19fe1e4ad58798
@@@ -1,6 -1,6 +1,6 @@@
 -import { resolveDynamicComponent } from '@vue/runtime-dom'
 +import { currentInstance, resolveDynamicComponent } from '@vue/runtime-dom'
- import { DynamicFragment, type VaporFragment } from './block'
+ import { DynamicFragment, type VaporFragment, insert } from './block'
 -import { createComponentWithFallback } from './component'
 +import { createComponentWithFallback, emptyContext } from './component'
  import { renderEffect } from './renderEffect'
  import type { RawProps } from './componentProps'
  import type { RawSlots } from './componentSlots'
@@@ -14,10 -28,9 +28,11 @@@ export function createDynamicComponent
    const frag = __DEV__
      ? new DynamicFragment('dynamic-component')
      : new DynamicFragment()
    renderEffect(() => {
      const value = getter()
 +    const appContext =
 +      (currentInstance && currentInstance.appContext) || emptyContext
      frag.update(
        () =>
          createComponentWithFallback(
index 92bfe562586d0e465d5fca49f3b897e4dbc83124,62529149ad4ce09256fe2f3d931f1541b86c336e..f60294f29f1e599b8df70816389a612d8072ad35
@@@ -22,7 -24,12 +24,13 @@@ import { currentInstance, isVaporCompon
  import type { DynamicSlot } from './componentSlots'
  import { renderEffect } from './renderEffect'
  import { VaporVForFlags } from '../../shared/src/vaporFlags'
 +import { applyTransitionHooks } from './components/Transition'
+ import { isHydrating, locateHydrationNode } from './dom/hydration'
+ import {
+   insertionAnchor,
+   insertionParent,
+   resetInsertionState,
+ } from './insertionState'
  
  class ForBlock extends VaporFragment {
    scope: EffectScope | undefined
index e1e1feb9e6daaa6f67edc0e825e59c10cb5f562d,af15133dbe54f7510deb7abc2bf85e88f76f6250..a407268eb56be282b819373d33ff77d4ac8b0946
@@@ -58,7 -58,12 +58,13 @@@ import 
    getSlot,
  } from './componentSlots'
  import { hmrReload, hmrRerender } from './hmr'
 +import { createElement } from './dom/node'
+ import { isHydrating, locateHydrationNode } from './dom/hydration'
+ import {
+   insertionAnchor,
+   insertionParent,
+   resetInsertionState,
+ } from './insertionState'
  
  export { currentInstance } from '@vue/runtime-dom'
  
@@@ -234,12 -255,16 +256,12 @@@ export function createComponent
    if (
      instance.hasFallthrough &&
      component.inheritAttrs !== false &&
-     instance.block instanceof Element &&
      Object.keys(instance.attrs).length
    ) {
-     renderEffect(() =>
-       applyFallthroughProps(instance.block as Element, instance.attrs),
-     )
+     const el = getRootElement(instance)
+     if (el) {
 -      renderEffect(() => {
 -        isApplyingFallthroughProps = true
 -        setDynamicProps(el, [instance.attrs])
 -        isApplyingFallthroughProps = false
 -      })
++      renderEffect(() => applyFallthroughProps(el, instance.attrs))
+     }
    }
  
    resetTracking()
@@@ -453,13 -473,20 +479,21 @@@ export function createComponentWithFall
    rawProps?: LooseRawProps | null,
    rawSlots?: LooseRawSlots | null,
    isSingleRoot?: boolean,
 +  appContext?: GenericAppContext,
  ): HTMLElement | VaporComponentInstance {
    if (!isString(comp)) {
 -    return createComponent(comp, rawProps, rawSlots, isSingleRoot)
 +    return createComponent(comp, rawProps, rawSlots, isSingleRoot, appContext)
    }
  
 -  const el = document.createElement(comp)
 +  const el = createElement(comp)
+   const _insertionParent = insertionParent
+   const _insertionAnchor = insertionAnchor
+   if (isHydrating) {
+     locateHydrationNode()
+   } else {
+     resetInsertionState()
+   }
    // mark single root
    ;(el as any).$root = isSingleRoot
  
index 14cace63c712007967df5882fe2376e1a7ec8438,b78ca4e52cfb8fa988d1de60ad7879a00f8f3566..7bfbca4e52b862a303f3f0ead9af091053201ba2
@@@ -1,9 -1,5 +1,5 @@@
- import {
-   adoptHydrationNode,
-   currentHydrationNode,
-   isHydrating,
- } from './hydration'
 +import { child, createElement, createTextNode } from './node'
+ import { adoptTemplate, currentHydrationNode, isHydrating } from './hydration'
 -import { child, createTextNode } from './node'
  
  let t: HTMLTemplateElement
  
Simple merge
index 3c3ae58e92c908f57a8eeeefd2bcb39c7d352e25,b916a2c8ebb0106b1d7542db2a83c17005c2649e..15e595f3ec8c04339d01e6a405779f283e19c9ce
@@@ -7,15 -7,15 +7,17 @@@ import 
    type RendererInternals,
    type ShallowRef,
    type Slots,
 +  type TransitionHooks,
    type VNode,
    type VaporInteropInterface,
+   createInternalObject,
    createVNode,
    currentInstance,
    ensureRenderer,
+   isEmitListener,
    onScopeDispose,
    renderSlot,
 +  setTransitionHooks as setVNodeTransitionHooks,
    shallowRef,
    simpleSetCurrentInstance,
  } from '@vue/runtime-dom'