mirror of https://github.com/vuejs/core.git
fix(hydration): skip lazy hydration for patched components (#13283)
close #13255
This commit is contained in:
parent
b9910755a5
commit
80055fddfb
|
@ -4,6 +4,7 @@ import {
|
||||||
type ComponentOptions,
|
type ComponentOptions,
|
||||||
type ConcreteComponent,
|
type ConcreteComponent,
|
||||||
currentInstance,
|
currentInstance,
|
||||||
|
getComponentName,
|
||||||
isInSSRComponentSetup,
|
isInSSRComponentSetup,
|
||||||
} from './component'
|
} from './component'
|
||||||
import { isFunction, isObject } from '@vue/shared'
|
import { isFunction, isObject } from '@vue/shared'
|
||||||
|
@ -121,14 +122,27 @@ export function defineAsyncComponent<
|
||||||
__asyncLoader: load,
|
__asyncLoader: load,
|
||||||
|
|
||||||
__asyncHydrate(el, instance, hydrate) {
|
__asyncHydrate(el, instance, hydrate) {
|
||||||
|
let patched = false
|
||||||
const doHydrate = hydrateStrategy
|
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),
|
forEachElement(el, cb),
|
||||||
)
|
)
|
||||||
if (teardown) {
|
if (teardown) {
|
||||||
;(instance.bum || (instance.bum = [])).push(teardown)
|
;(instance.bum || (instance.bum = [])).push(teardown)
|
||||||
}
|
}
|
||||||
|
;(instance.u || (instance.u = [])).push(() => (patched = true))
|
||||||
}
|
}
|
||||||
: hydrate
|
: hydrate
|
||||||
if (resolvedComp) {
|
if (resolvedComp) {
|
||||||
|
|
|
@ -15,13 +15,17 @@
|
||||||
} = Vue
|
} = Vue
|
||||||
|
|
||||||
const Comp = {
|
const Comp = {
|
||||||
setup() {
|
props: {
|
||||||
|
value: Boolean,
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
const count = ref(0)
|
const count = ref(0)
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
console.log('hydrated')
|
console.log('hydrated')
|
||||||
window.isHydrated = true
|
window.isHydrated = true
|
||||||
})
|
})
|
||||||
return () => {
|
return () => {
|
||||||
|
props.value
|
||||||
return h('button', { onClick: () => count.value++ }, count.value)
|
return h('button', { onClick: () => count.value++ }, count.value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -37,7 +41,9 @@
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.isRootMounted = true
|
window.isRootMounted = true
|
||||||
})
|
})
|
||||||
return () => h(AsyncComp)
|
|
||||||
|
const show = (window.show = ref(true))
|
||||||
|
return () => h(AsyncComp, { value: show.value })
|
||||||
},
|
},
|
||||||
}).mount('#app')
|
}).mount('#app')
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -86,6 +86,36 @@ describe('async component hydration strategies', () => {
|
||||||
await assertHydrationSuccess()
|
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 () => {
|
test('interaction', async () => {
|
||||||
await goToCase('interaction')
|
await goToCase('interaction')
|
||||||
await page().waitForFunction(() => window.isRootMounted)
|
await page().waitForFunction(() => window.isRootMounted)
|
||||||
|
|
Loading…
Reference in New Issue