Skip to content

Commit

Permalink
fix(hydration): properly handle optimized mode during hydrate node (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
edison1105 committed Apr 15, 2024
1 parent 0cef65c commit 2ec06fd
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 0 deletions.
81 changes: 81 additions & 0 deletions packages/runtime-core/__tests__/hydration.spec.ts
Expand Up @@ -7,7 +7,10 @@ import {
Teleport,
Transition,
type VNode,
createBlock,
createCommentVNode,
createElementBlock,
createElementVNode,
createSSRApp,
createStaticVNode,
createTextVNode,
Expand All @@ -17,16 +20,19 @@ import {
h,
nextTick,
onMounted,
openBlock,
ref,
renderSlot,
useCssVars,
vModelCheckbox,
vShow,
withCtx,
withDirectives,
} from '@vue/runtime-dom'
import { type SSRContext, renderToString } from '@vue/server-renderer'
import { PatchFlags } from '@vue/shared'
import { vShowOriginalDisplay } from '../../runtime-dom/src/directives/vShow'
import { expect } from 'vitest'

function mountWithHydration(html: string, render: () => any) {
const container = document.createElement('div')
Expand Down Expand Up @@ -1292,6 +1298,81 @@ describe('SSR hydration', () => {
`)
})

// #10607
test('update component stable slot (prod + optimized mode)', async () => {
__DEV__ = false
const container = document.createElement('div')
container.innerHTML = `<template><div show="false"><!--[--><div><div><!----></div></div><div>0</div><!--]--></div></template>`
const Comp = {
render(this: any) {
return (
openBlock(),
createElementBlock('div', null, [renderSlot(this.$slots, 'default')])
)
},
}
const show = ref(false)
const clicked = ref(false)

const Wrapper = {
setup() {
const items = ref<number[]>([])
onMounted(() => {
items.value = [1]
})
return () => {
return (
openBlock(),
createBlock(Comp, null, {
default: withCtx(() => [
createElementVNode('div', null, [
createElementVNode('div', null, [
clicked.value
? (openBlock(),
createElementBlock('div', { key: 0 }, 'foo'))
: createCommentVNode('v-if', true),
]),
]),
createElementVNode(
'div',
null,
items.value.length,
1 /* TEXT */,
),
]),
_: 1 /* STABLE */,
})
)
}
},
}
createSSRApp({
components: { Wrapper },
data() {
return { show }
},
template: `<Wrapper :show="show"/>`,
}).mount(container)

await nextTick()
expect(container.innerHTML).toBe(
`<div show="false"><!--[--><div><div><!----></div></div><div>1</div><!--]--></div>`,
)

show.value = true
await nextTick()
expect(async () => {
clicked.value = true
await nextTick()
}).not.toThrow("Cannot read properties of null (reading 'insertBefore')")

await nextTick()
expect(container.innerHTML).toBe(
`<div show="true"><!--[--><div><div><div>foo</div></div></div><div>1</div><!--]--></div>`,
)
__DEV__ = true
})

describe('mismatch handling', () => {
test('text node', () => {
const { container } = mountWithHydration(`foo`, () => 'bar')
Expand Down
1 change: 1 addition & 0 deletions packages/runtime-core/src/hydration.ts
Expand Up @@ -120,6 +120,7 @@ export function createHydrationFunctions(
slotScopeIds: string[] | null,
optimized = false,
): Node | null => {
optimized = optimized || !!vnode.dynamicChildren
const isFragmentStart = isComment(node) && node.data === '['
const onMismatch = () =>
handleMismatch(
Expand Down

0 comments on commit 2ec06fd

Please sign in to comment.