Skip to content

Commit 2ec06fd

Browse files
authoredApr 15, 2024··
fix(hydration): properly handle optimized mode during hydrate node (#10638)
close #10607
1 parent 0cef65c commit 2ec06fd

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed
 

‎packages/runtime-core/__tests__/hydration.spec.ts

+81
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import {
77
Teleport,
88
Transition,
99
type VNode,
10+
createBlock,
1011
createCommentVNode,
12+
createElementBlock,
13+
createElementVNode,
1114
createSSRApp,
1215
createStaticVNode,
1316
createTextVNode,
@@ -17,16 +20,19 @@ import {
1720
h,
1821
nextTick,
1922
onMounted,
23+
openBlock,
2024
ref,
2125
renderSlot,
2226
useCssVars,
2327
vModelCheckbox,
2428
vShow,
29+
withCtx,
2530
withDirectives,
2631
} from '@vue/runtime-dom'
2732
import { type SSRContext, renderToString } from '@vue/server-renderer'
2833
import { PatchFlags } from '@vue/shared'
2934
import { vShowOriginalDisplay } from '../../runtime-dom/src/directives/vShow'
35+
import { expect } from 'vitest'
3036

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

1301+
// #10607
1302+
test('update component stable slot (prod + optimized mode)', async () => {
1303+
__DEV__ = false
1304+
const container = document.createElement('div')
1305+
container.innerHTML = `<template><div show="false"><!--[--><div><div><!----></div></div><div>0</div><!--]--></div></template>`
1306+
const Comp = {
1307+
render(this: any) {
1308+
return (
1309+
openBlock(),
1310+
createElementBlock('div', null, [renderSlot(this.$slots, 'default')])
1311+
)
1312+
},
1313+
}
1314+
const show = ref(false)
1315+
const clicked = ref(false)
1316+
1317+
const Wrapper = {
1318+
setup() {
1319+
const items = ref<number[]>([])
1320+
onMounted(() => {
1321+
items.value = [1]
1322+
})
1323+
return () => {
1324+
return (
1325+
openBlock(),
1326+
createBlock(Comp, null, {
1327+
default: withCtx(() => [
1328+
createElementVNode('div', null, [
1329+
createElementVNode('div', null, [
1330+
clicked.value
1331+
? (openBlock(),
1332+
createElementBlock('div', { key: 0 }, 'foo'))
1333+
: createCommentVNode('v-if', true),
1334+
]),
1335+
]),
1336+
createElementVNode(
1337+
'div',
1338+
null,
1339+
items.value.length,
1340+
1 /* TEXT */,
1341+
),
1342+
]),
1343+
_: 1 /* STABLE */,
1344+
})
1345+
)
1346+
}
1347+
},
1348+
}
1349+
createSSRApp({
1350+
components: { Wrapper },
1351+
data() {
1352+
return { show }
1353+
},
1354+
template: `<Wrapper :show="show"/>`,
1355+
}).mount(container)
1356+
1357+
await nextTick()
1358+
expect(container.innerHTML).toBe(
1359+
`<div show="false"><!--[--><div><div><!----></div></div><div>1</div><!--]--></div>`,
1360+
)
1361+
1362+
show.value = true
1363+
await nextTick()
1364+
expect(async () => {
1365+
clicked.value = true
1366+
await nextTick()
1367+
}).not.toThrow("Cannot read properties of null (reading 'insertBefore')")
1368+
1369+
await nextTick()
1370+
expect(container.innerHTML).toBe(
1371+
`<div show="true"><!--[--><div><div><div>foo</div></div></div><div>1</div><!--]--></div>`,
1372+
)
1373+
__DEV__ = true
1374+
})
1375+
12951376
describe('mismatch handling', () => {
12961377
test('text node', () => {
12971378
const { container } = mountWithHydration(`foo`, () => 'bar')

‎packages/runtime-core/src/hydration.ts

+1
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export function createHydrationFunctions(
120120
slotScopeIds: string[] | null,
121121
optimized = false,
122122
): Node | null => {
123+
optimized = optimized || !!vnode.dynamicChildren
123124
const isFragmentStart = isComment(node) && node.data === '['
124125
const onMismatch = () =>
125126
handleMismatch(

0 commit comments

Comments
 (0)
Please sign in to comment.