Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(hydration): avoid observing non-Element node #11954

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
29 changes: 16 additions & 13 deletions packages/runtime-core/src/hydrationStrategies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,21 +86,24 @@ export const hydrateOnInteraction: HydrationStrategyFactory<
}

export function forEachElement(node: Node, cb: (el: Element) => void): void {
// fragment
if (isComment(node) && node.data === '[') {
let depth = 1
let next = node.nextSibling
while (next) {
if (next.nodeType === DOMNodeTypes.ELEMENT) {
cb(next as Element)
} else if (isComment(next)) {
if (next.data === ']') {
if (--depth === 0) break
} else if (next.data === '[') {
depth++
// #11952
if (isComment(node)) {
edison1105 marked this conversation as resolved.
Show resolved Hide resolved
// fragment
if (node.data === '[') {
let depth = 1
let next = node.nextSibling
while (next) {
if (next.nodeType === DOMNodeTypes.ELEMENT) {
cb(next as Element)
} else if (isComment(next)) {
if (next.data === ']') {
if (--depth === 0) break
} else if (next.data === '[') {
depth++
}
}
next = next.nextSibling
}
next = next.nextSibling
}
} else {
cb(node as Element)
Expand Down
35 changes: 32 additions & 3 deletions packages/vue/__tests__/e2e/hydration-strat-visible.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,27 @@
<script>
const rootMargin = location.search.match(/rootMargin=(\d+)/)?.[1] ?? 0
const isFragment = location.search.includes('?fragment')
const isVIf = location.search.includes('?v-if')
if (isFragment) {
document.getElementById('app').innerHTML =
`<!--[--><!--[--><span>one</span><!--]--><button>0</button><span>two</span><!--]-->`
}
if (isVIf) {
document.getElementById('app').innerHTML = `<!---->`
}

window.isHydrated = false

const {
createSSRApp,
defineAsyncComponent,
h,
ref,
onMounted,
hydrateOnVisible,
createCommentVNode,
} = Vue
window.propsShow = ref(false)

const Comp = {
setup() {
Expand All @@ -47,18 +54,40 @@
}
},
}
const CompWithRootVIf = {
props: ['show'],
setup(props) {
const count = ref(0)
onMounted(() => {
console.log('hydrated')
window.isHydrated = true
})
return () => {
const button = h(
'button',
{ onClick: () => count.value++ },
count.value,
)
return props.show ? button : createCommentVNode('v-if', true)
}
},
}

const AsyncComp = defineAsyncComponent({
loader: () => Promise.resolve(Comp),
hydrate: hydrateOnVisible({ rootMargin: rootMargin + 'px' }),
loader: () => Promise.resolve(isVIf ? CompWithRootVIf : Comp),
hydrate: isVIf
? hydrateOnVisible()
: hydrateOnVisible({ rootMargin: rootMargin + 'px' }),
})

createSSRApp({
setup() {
onMounted(() => {
window.isRootMounted = true
})
return () => h(AsyncComp)
return isVIf
? () => h(AsyncComp, { show: window.propsShow.value })
: () => h(AsyncComp)
},
}).mount('#app')
</script>
10 changes: 10 additions & 0 deletions packages/vue/__tests__/e2e/hydrationStrategies.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ declare const window: Window & {
isRootMounted: boolean
teardownCalled?: boolean
show: Ref<boolean>
propsShow: Ref<boolean>
}

describe('async component hydration strategies', () => {
Expand Down Expand Up @@ -65,6 +66,15 @@ describe('async component hydration strategies', () => {
await assertHydrationSuccess()
})

test('visible (root v-if)', async () => {
await goToCase('visible', '?v-if')
await page().waitForFunction(() => window.isRootMounted)
expect(await page().evaluate(() => window.isHydrated)).toBe(false)
await page().evaluate(() => {
window.propsShow.value = true
})
await assertHydrationSuccess()
})
test('media query', async () => {
await goToCase('media')
await page().waitForFunction(() => window.isRootMounted)
Expand Down