From bc31190831511c38326cdd8ce599dbc151168929 Mon Sep 17 00:00:00 2001 From: daiwei Date: Tue, 6 May 2025 08:47:49 +0800 Subject: [PATCH 1/3] fix(hydration): skip lazy hydration for patched components --- packages/runtime-core/src/apiAsyncComponent.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/src/apiAsyncComponent.ts b/packages/runtime-core/src/apiAsyncComponent.ts index 199b451f6..cb675f06e 100644 --- a/packages/runtime-core/src/apiAsyncComponent.ts +++ b/packages/runtime-core/src/apiAsyncComponent.ts @@ -4,6 +4,7 @@ import { type ComponentOptions, type ConcreteComponent, currentInstance, + getComponentName, isInSSRComponentSetup, } from './component' import { isFunction, isObject } from '@vue/shared' @@ -121,14 +122,27 @@ export function defineAsyncComponent< __asyncLoader: load, __asyncHydrate(el, instance, hydrate) { + let patched = false const doHydrate = hydrateStrategy ? () => { - const teardown = hydrateStrategy(hydrate, cb => + const performHydrate = () => { + // skip hydration if the component has been patched + if (__DEV__ && patched) { + warn( + `Skipping lazy hydration for component '${getComponentName(resolvedComp!)}': ` + + `it was updated before lazy hydration performed.`, + ) + return + } + hydrate() + } + const teardown = hydrateStrategy(performHydrate, cb => forEachElement(el, cb), ) if (teardown) { ;(instance.bum || (instance.bum = [])).push(teardown) } + ;(instance.u || (instance.u = [])).push(() => (patched = true)) } : hydrate if (resolvedComp) { From 1e876f7c949ae6912b68edaebd1fcc1e719f5cfb Mon Sep 17 00:00:00 2001 From: daiwei Date: Tue, 6 May 2025 09:40:01 +0800 Subject: [PATCH 2/3] test: add e2e test --- .../__tests__/e2e/hydration-strat-media.html | 10 +++++-- .../__tests__/e2e/hydrationStrategies.spec.ts | 30 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/packages/vue/__tests__/e2e/hydration-strat-media.html b/packages/vue/__tests__/e2e/hydration-strat-media.html index c04cdb2a7..536f2d3bd 100644 --- a/packages/vue/__tests__/e2e/hydration-strat-media.html +++ b/packages/vue/__tests__/e2e/hydration-strat-media.html @@ -15,13 +15,17 @@ } = Vue const Comp = { - setup() { + props: { + value: Boolean + }, + setup(props) { const count = ref(0) onMounted(() => { console.log('hydrated') window.isHydrated = true }) return () => { + props.value return h('button', { onClick: () => count.value++ }, count.value) } }, @@ -37,7 +41,9 @@ onMounted(() => { window.isRootMounted = true }) - return () => h(AsyncComp) + + const show = (window.show = ref(true)) + return () => h(AsyncComp,{ value: show.value }) }, }).mount('#app') diff --git a/packages/vue/__tests__/e2e/hydrationStrategies.spec.ts b/packages/vue/__tests__/e2e/hydrationStrategies.spec.ts index 69934d959..d792edf19 100644 --- a/packages/vue/__tests__/e2e/hydrationStrategies.spec.ts +++ b/packages/vue/__tests__/e2e/hydrationStrategies.spec.ts @@ -86,6 +86,36 @@ describe('async component hydration strategies', () => { await assertHydrationSuccess() }) + // #13255 + test('media query (patched before hydration)', async () => { + const spy = vi.fn() + const currentPage = page() + currentPage.on('pageerror', spy) + + const warn: any[] = [] + currentPage.on('console', e => warn.push(e.text())) + + await goToCase('media') + await page().waitForFunction(() => window.isRootMounted) + expect(await page().evaluate(() => window.isHydrated)).toBe(false) + + // patch + await page().evaluate(() => (window.show.value = false)) + await click('button') + expect(await text('button')).toBe('1') + + // resize + await page().setViewport({ width: 400, height: 600 }) + await page().waitForFunction(() => window.isHydrated) + await assertHydrationSuccess('2') + + expect(spy).toBeCalledTimes(0) + currentPage.off('pageerror', spy) + expect( + warn.some(w => w.includes('Skipping lazy hydration for component')), + ).toBe(true) + }) + test('interaction', async () => { await goToCase('interaction') await page().waitForFunction(() => window.isRootMounted) From 24179afcf555af1ae9735d7d8e79b26c74251fb7 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 01:40:54 +0000 Subject: [PATCH 3/3] [autofix.ci] apply automated fixes --- packages/vue/__tests__/e2e/hydration-strat-media.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vue/__tests__/e2e/hydration-strat-media.html b/packages/vue/__tests__/e2e/hydration-strat-media.html index 536f2d3bd..954a73d04 100644 --- a/packages/vue/__tests__/e2e/hydration-strat-media.html +++ b/packages/vue/__tests__/e2e/hydration-strat-media.html @@ -16,7 +16,7 @@ const Comp = { props: { - value: Boolean + value: Boolean, }, setup(props) { const count = ref(0) @@ -43,7 +43,7 @@ }) const show = (window.show = ref(true)) - return () => h(AsyncComp,{ value: show.value }) + return () => h(AsyncComp, { value: show.value }) }, }).mount('#app')