From: daiwei Date: Wed, 18 Jun 2025 09:36:39 +0000 (+0800) Subject: chore: Merge branch 'edison/fix/dynamicComponentWithVHtml' into edison/testVapor X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8bf75a77dbdb410a8ed412b2c6fd8197b12323bd;p=thirdparty%2Fvuejs%2Fcore.git chore: Merge branch 'edison/fix/dynamicComponentWithVHtml' into edison/testVapor --- 8bf75a77dbdb410a8ed412b2c6fd8197b12323bd diff --cc packages/runtime-core/__tests__/hydration.spec.ts index 793c11cede,20519cf997..d5c418c16e --- a/packages/runtime-core/__tests__/hydration.spec.ts +++ b/packages/runtime-core/__tests__/hydration.spec.ts @@@ -1843,36 -1846,60 +1846,90 @@@ describe('SSR hydration', () => } }) + describe('dynamic anchor', () => { + test('two consecutive components', () => { + const Comp = { + render() { + return createTextVNode('foo') + }, + } + const { vnode, container } = mountWithHydration( + `
foofoo
`, + () => h('div', null, [h('span'), h(Comp), h(Comp), h('span')]), + ) + expect(vnode.el).toBe(container.firstChild) + expect(`Hydration children mismatch`).not.toHaveBeenWarned() + }) + + test('multiple consecutive components', () => { + const Comp = { + render() { + return createTextVNode('foo') + }, + } + const { vnode, container } = mountWithHydration( + `
foofoofoo
`, + () => h('div', null, [h('span'), h(Comp), h(Comp), h(Comp), h('span')]), + ) + expect(vnode.el).toBe(container.firstChild) + expect(`Hydration children mismatch`).not.toHaveBeenWarned() + }) + }) + + test('hmr reload child wrapped in KeepAlive', async () => { + const id = 'child-reload' + const Child = { + __hmrId: id, + template: `
foo
`, + } + createRecord(id, Child) + + const appId = 'test-app-id' + const App = { + __hmrId: appId, + components: { Child }, + template: ` +
+ + + +
+ `, + } + + const root = document.createElement('div') + root.innerHTML = await renderToString(h(App)) + createSSRApp(App).mount(root) + expect(root.innerHTML).toBe('
foo
') + + reload(id, { + __hmrId: id, + template: `
bar
`, + }) + await nextTick() + expect(root.innerHTML).toBe('
bar
') + }) + + test('hmr root reload', async () => { + const appId = 'test-app-id' + const App = { + __hmrId: appId, + template: `
foo
`, + } + + const root = document.createElement('div') + root.innerHTML = await renderToString(h(App)) + createSSRApp(App).mount(root) + expect(root.innerHTML).toBe('
foo
') + + reload(appId, { + __hmrId: appId, + template: `
bar
`, + }) + await nextTick() + expect(root.innerHTML).toBe('
bar
') + }) + describe('mismatch handling', () => { test('text node', () => { const { container } = mountWithHydration(`foo`, () => 'bar') diff --cc packages/runtime-dom/src/components/TransitionGroup.ts index 8e38dbf0f2,72af535d38..abf3e09542 --- a/packages/runtime-dom/src/components/TransitionGroup.ts +++ b/packages/runtime-dom/src/components/TransitionGroup.ts @@@ -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 () => { diff --cc packages/runtime-vapor/src/apiCreateFor.ts index 7bde89fbb1,62529149ad..19ee718271 --- a/packages/runtime-vapor/src/apiCreateFor.ts +++ b/packages/runtime-vapor/src/apiCreateFor.ts @@@ -8,14 -9,9 +9,15 @@@ import shallowReadArray, shallowRef, toReactive, + toReadonly, } from '@vue/reactivity' -import { getSequence, isArray, isObject, isString } from '@vue/shared' +import { + FOR_ANCHOR_LABEL, + getSequence, + isArray, + isObject, + isString, +} from '@vue/shared' import { createComment, createTextNode } from './dom/node' import { type Block, diff --cc packages/runtime-vapor/src/component.ts index 55fdcffeb0,c6602ec961..4cc7051afc --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@@ -491,13 -471,20 +491,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 _insertionParent = insertionParent + const _insertionAnchor = insertionAnchor + if (isHydrating) { + locateHydrationNode() + } else { + resetInsertionState() + } + - const el = document.createElement(comp) + const el = createElement(comp) // mark single root ;(el as any).$root = isSingleRoot diff --cc packages/server-renderer/__tests__/ssrSlot.spec.ts index d17e34bc7c,4cc7fd97ef..e843522548 --- a/packages/server-renderer/__tests__/ssrSlot.spec.ts +++ b/packages/server-renderer/__tests__/ssrSlot.spec.ts @@@ -156,6 -154,38 +156,38 @@@ describe('ssr: slot', () => ).toBe(`

1

2

`) }) + // #12438 + test('async component slot with v-if true', async () => { + const Layout = defineAsyncComponent(() => + Promise.resolve({ + template: `
default header
`, + }), + ) + const LayoutLoader = { + setup(_: any, context: any) { + return () => h(Layout, {}, context.slots) + }, + } + expect( + await renderToString( + createApp({ + components: { + LayoutLoader, + }, + template: ` + + + + + + `, + }), + ), - ).toBe(`
new header
`) ++ ).toBe(`
new header
`) + }) + // #11326 test('dynamic component slot', async () => { expect(