]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
chore: Merge branch 'vapor' into edison/fix/vaporOnce
authordaiwei <daiwei521@126.com>
Fri, 20 Jun 2025 06:26:52 +0000 (14:26 +0800)
committerdaiwei <daiwei521@126.com>
Fri, 20 Jun 2025 06:26:52 +0000 (14:26 +0800)
1  2 
packages/runtime-vapor/__tests__/apiCreateDynamicComponent.spec.ts
packages/runtime-vapor/__tests__/component.spec.ts
packages/runtime-vapor/src/apiCreateDynamicComponent.ts
packages/runtime-vapor/src/component.ts
packages/runtime-vapor/src/componentProps.ts

index 448bd2c315bf9b7556614d4579eae02c5b049f41,e912af2851ab4719e0a198a51289a2f950f67626..89514e17701c7bf196b0f6957ceca59bbc3c3068
@@@ -55,41 -63,51 +63,89 @@@ describe('api: createDynamicComponent'
      expect(html()).toBe('<baz></baz><!--dynamic-component-->')
    })
  
 +  test('with v-once', async () => {
 +    const val = shallowRef<any>(A)
 +
 +    const { html } = define({
 +      setup() {
 +        return createDynamicComponent(() => val.value, null, null, true, true)
 +      },
 +    }).render()
 +
 +    expect(html()).toBe('AAA<!--dynamic-component-->')
 +
 +    val.value = B
 +    await nextTick()
 +    expect(html()).toBe('AAA<!--dynamic-component-->') // still AAA
 +  })
 +
 +  test('fallback with v-once', async () => {
 +    const val = shallowRef<any>('button')
 +    const id = ref(0)
 +    const { html } = define({
 +      setup() {
 +        return createDynamicComponent(
 +          () => val.value,
 +          { id: () => id.value },
 +          null,
 +          true,
 +          true,
 +        )
 +      },
 +    }).render()
 +
 +    expect(html()).toBe('<button id="0"></button><!--dynamic-component-->')
 +
 +    id.value++
 +    await nextTick()
 +    expect(html()).toBe('<button id="0"></button><!--dynamic-component-->')
 +  })
++
+   test('render fallback with insertionState', async () => {
+     const { html, mount } = define({
+       setup() {
+         const html = ref('hi')
+         const n1 = template('<div></div>', true)() as any
+         setInsertionState(n1)
+         const n0 = createComponentWithFallback(
+           resolveDynamicComponent('button') as any,
+         ) as any
+         renderEffect(() => setHtml(n0, html.value))
+         return n1
+       },
+     }).create()
+     mount()
+     expect(html()).toBe('<div><button>hi</button></div>')
+   })
+   test('switch dynamic component children', async () => {
+     const CompA = defineVaporComponent({
+       setup() {
+         return template('<div>A</div>')()
+       },
+     })
+     const CompB = defineVaporComponent({
+       setup() {
+         return template('<div>B</div>')()
+       },
+     })
+     const current = shallowRef(CompA)
+     const { html } = define({
+       setup() {
+         const t1 = template('<div></div>')
+         const n2 = t1() as any
+         setInsertionState(n2)
+         createDynamicComponent(() => current.value)
+         return n2
+       },
+     }).render()
+     expect(html()).toBe('<div><div>A</div><!--dynamic-component--></div>')
+     current.value = CompB
+     await nextTick()
+     expect(html()).toBe('<div><div>B</div><!--dynamic-component--></div>')
+   })
  })
index 871df18077583069225ace9259adb77f52aa3426,22294b1e7356d45928b435c752984b6498bd2d71..b8a0180762a2de4f57f684e51bdb1acff3fcd387
@@@ -13,8 -13,8 +14,9 @@@ import 
    createComponent,
    createIf,
    createTextNode,
 +  defineVaporComponent,
    renderEffect,
+   setInsertionState,
    template,
  } from '../src'
  import { makeRender } from './_utils'
index a1b1e3e98f3b53517b7e47c18d6a623bafee788e,945e0f38d8729b9f97fb2ef42296d8dcf62b453f..9e51e81d97634175c08454c2d5752f2a76f6c0a3
@@@ -10,8 -16,15 +16,16 @@@ export function createDynamicComponent
    rawProps?: RawProps | null,
    rawSlots?: RawSlots | null,
    isSingleRoot?: boolean,
 +  once?: boolean,
  ): VaporFragment {
+   const _insertionParent = insertionParent
+   const _insertionAnchor = insertionAnchor
+   if (isHydrating) {
+     locateHydrationNode()
+   } else {
+     resetInsertionState()
+   }
    const frag = __DEV__
      ? new DynamicFragment('dynamic-component')
      : new DynamicFragment()
          ),
        value,
      )
 -  })
 +  }
 +
 +  if (once) renderFn()
 +  else renderEffect(renderFn)
  
+   if (!isHydrating && _insertionParent) {
+     insert(frag, _insertionParent, _insertionAnchor)
+   }
    return frag
  }
index 27f14f712289f80f96fd5a2d09a38d511284f620,af15133dbe54f7510deb7abc2bf85e88f76f6250..da65f24d55f8f93f4ff23169b34d0333c753f70d
@@@ -468,12 -473,19 +476,20 @@@ export function createComponentWithFall
    rawProps?: LooseRawProps | null,
    rawSlots?: LooseRawSlots | null,
    isSingleRoot?: boolean,
 +  once?: boolean,
  ): HTMLElement | VaporComponentInstance {
    if (!isString(comp)) {
 -    return createComponent(comp, rawProps, rawSlots, isSingleRoot)
 +    return createComponent(comp, rawProps, rawSlots, isSingleRoot, once)
    }
  
+   const _insertionParent = insertionParent
+   const _insertionAnchor = insertionAnchor
+   if (isHydrating) {
+     locateHydrationNode()
+   } else {
+     resetInsertionState()
+   }
    const el = document.createElement(comp)
    // mark single root
    ;(el as any).$root = isSingleRoot
index dbc1386e3969afc7da83b7c7bda1647a88b26d0f,9cf65c57143a4f72b3b466cd5732bde7cd5cead1..6d1c686c94fff3b05bceedc3abdf132e522af494
@@@ -21,9 -21,9 +21,10 @@@ import 
    validateProps,
    warn,
  } from '@vue/runtime-dom'
+ import { ReactiveFlags } from '@vue/reactivity'
  import { normalizeEmitsOptions } from './componentEmits'
  import { renderEffect } from './renderEffect'
 +import { pauseTracking, resetTracking } from '@vue/reactivity'
  
  export type RawProps = Record<string, () => unknown> & {
    // generated by compiler for :[key]="x" or v-bind="x"