Skip to content

Commit

Permalink
fix(ssr): avoid updating subtree of async component if it is resolved (
Browse files Browse the repository at this point in the history
  • Loading branch information
edison1105 authored Nov 15, 2024
1 parent 1f75d4e commit da7ad5e
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 2 deletions.
78 changes: 78 additions & 0 deletions packages/runtime-core/__tests__/hydration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1324,6 +1324,84 @@ describe('SSR hydration', () => {
resolve({})
})

//#12362
test('nested async wrapper', async () => {
const Toggle = defineAsyncComponent(
() =>
new Promise(r => {
r(
defineComponent({
setup(_, { slots }) {
const show = ref(false)
onMounted(() => {
nextTick(() => {
show.value = true
})
})
return () =>
withDirectives(
h('div', null, [renderSlot(slots, 'default')]),
[[vShow, show.value]],
)
},
}) as any,
)
}),
)

const Wrapper = defineAsyncComponent(() => {
return new Promise(r => {
r(
defineComponent({
render(this: any) {
return renderSlot(this.$slots, 'default')
},
}) as any,
)
})
})

const count = ref(0)
const fn = vi.fn()
const Child = {
setup() {
onMounted(() => {
fn()
count.value++
})
return () => h('div', count.value)
},
}

const App = {
render() {
return h(Toggle, null, {
default: () =>
h(Wrapper, null, {
default: () =>
h(Wrapper, null, {
default: () => h(Child),
}),
}),
})
},
}

const root = document.createElement('div')
root.innerHTML = await renderToString(h(App))
expect(root.innerHTML).toMatchInlineSnapshot(
`"<div style="display:none;"><!--[--><!--[--><!--[--><div>0</div><!--]--><!--]--><!--]--></div>"`,
)

createSSRApp(App).mount(root)
await nextTick()
await nextTick()
expect(root.innerHTML).toMatchInlineSnapshot(
`"<div style=""><!--[--><!--[--><!--[--><div>1</div><!--]--><!--]--><!--]--></div>"`,
)
expect(fn).toBeCalledTimes(1)
})

test('unmount async wrapper before load (fragment)', async () => {
let resolve: any
const AsyncComp = defineAsyncComponent(
Expand Down
7 changes: 5 additions & 2 deletions packages/runtime-core/src/hydration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
normalizeVNode,
} from './vnode'
import { flushPostFlushCbs } from './scheduler'
import type { ComponentInternalInstance } from './component'
import type { ComponentInternalInstance, ComponentOptions } from './component'
import { invokeDirectiveHook } from './directives'
import { warn } from './warning'
import {
Expand Down Expand Up @@ -308,7 +308,10 @@ export function createHydrationFunctions(
// if component is async, it may get moved / unmounted before its
// inner component is loaded, so we need to give it a placeholder
// vnode that matches its adopted DOM.
if (isAsyncWrapper(vnode)) {
if (
isAsyncWrapper(vnode) &&
!(vnode.type as ComponentOptions).__asyncResolved
) {
let subTree
if (isFragmentStart) {
subTree = createVNode(Fragment)
Expand Down

0 comments on commit da7ad5e

Please sign in to comment.