From 8968f6471e26895869c484dc080b96716ebef4aa Mon Sep 17 00:00:00 2001 From: daiwei Date: Tue, 2 Apr 2024 16:13:32 +0800 Subject: [PATCH 1/4] fix(hydration): properly handle optimized mode during hydrate node --- packages/runtime-core/src/hydration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index de02ae46d8a..6cf23191f2b 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -118,7 +118,7 @@ export function createHydrationFunctions( parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, slotScopeIds: string[] | null, - optimized = false, + optimized = !!vnode.dynamicChildren, ): Node | null => { const isFragmentStart = isComment(node) && node.data === '[' const onMismatch = () => From c0f333eb73468c6b7ca5be34e7c8fe5388dbe998 Mon Sep 17 00:00:00 2001 From: daiwei Date: Tue, 2 Apr 2024 16:33:42 +0800 Subject: [PATCH 2/4] chore: improve code --- packages/runtime-core/src/hydration.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/src/hydration.ts b/packages/runtime-core/src/hydration.ts index 6cf23191f2b..20f64a80d49 100644 --- a/packages/runtime-core/src/hydration.ts +++ b/packages/runtime-core/src/hydration.ts @@ -118,8 +118,9 @@ export function createHydrationFunctions( parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, slotScopeIds: string[] | null, - optimized = !!vnode.dynamicChildren, + optimized = false, ): Node | null => { + optimized = optimized || !!vnode.dynamicChildren const isFragmentStart = isComment(node) && node.data === '[' const onMismatch = () => handleMismatch( From 4f3e4b88c3f477715843c92ee2431814cbd8f922 Mon Sep 17 00:00:00 2001 From: edison1105 Date: Tue, 2 Apr 2024 22:09:04 +0800 Subject: [PATCH 3/4] test: add test case --- .../runtime-core/__tests__/hydration.spec.ts | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts index 6caa2442e18..dafee0cfa1b 100644 --- a/packages/runtime-core/__tests__/hydration.spec.ts +++ b/packages/runtime-core/__tests__/hydration.spec.ts @@ -7,7 +7,10 @@ import { Teleport, Transition, type VNode, + createBlock, createCommentVNode, + createElementBlock, + createElementVNode, createSSRApp, createStaticVNode, createTextVNode, @@ -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') @@ -1292,6 +1298,76 @@ describe('SSR hydration', () => { `) }) + // #10607 + test('update component stable slot (prod + optimized mode)', async () => { + __DEV__ = false + const container = document.createElement('div') + container.innerHTML = `` + 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([]) + 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: ``, + }).mount(container) + + await nextTick() + expect(container.innerHTML).toBe( + `
1
`, + ) + + show.value = true + await nextTick() + expect(async () => { + clicked.value = true + await nextTick() + }).not.toThrow("Cannot read properties of null (reading 'insertBefore')") + __DEV__ = true + }) + describe('mismatch handling', () => { test('text node', () => { const { container } = mountWithHydration(`foo`, () => 'bar') From 8e74fab999cf5ad06e1e4409639c3722191627f8 Mon Sep 17 00:00:00 2001 From: edison1105 Date: Tue, 2 Apr 2024 22:15:06 +0800 Subject: [PATCH 4/4] test: update test --- packages/runtime-core/__tests__/hydration.spec.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts index dafee0cfa1b..e0277622c13 100644 --- a/packages/runtime-core/__tests__/hydration.spec.ts +++ b/packages/runtime-core/__tests__/hydration.spec.ts @@ -1365,6 +1365,11 @@ describe('SSR hydration', () => { clicked.value = true await nextTick() }).not.toThrow("Cannot read properties of null (reading 'insertBefore')") + + await nextTick() + expect(container.innerHTML).toBe( + `
foo
1
`, + ) __DEV__ = true })