perf(hydration): avoid observer if element is in viewport (#11639)

This commit is contained in:
Michael Brevard 2024-09-18 10:44:23 +03:00 committed by Evan You
parent 9a36f2a0b8
commit e075dfad5c
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A

View File

@ -26,6 +26,16 @@ export const hydrateOnIdle: HydrationStrategyFactory<number> =
return () => cancelIdleCallback(id)
}
function elementIsVisibleInViewport(el: Element) {
const { top, left, bottom, right } = el.getBoundingClientRect()
// eslint-disable-next-line no-restricted-globals
const { innerHeight, innerWidth } = window
return (
((top > 0 && top < innerHeight) || (bottom > 0 && bottom < innerHeight)) &&
((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))
)
}
export const hydrateOnVisible: HydrationStrategyFactory<
IntersectionObserverInit
> = opts => (hydrate, forEach) => {
@ -37,7 +47,14 @@ export const hydrateOnVisible: HydrationStrategyFactory<
break
}
}, opts)
forEach(el => ob.observe(el))
forEach(el => {
if (elementIsVisibleInViewport(el)) {
hydrate()
ob.disconnect()
return false
}
ob.observe(el)
})
return () => ob.disconnect()
}
@ -85,14 +102,20 @@ export const hydrateOnInteraction: HydrationStrategyFactory<
return teardown
}
export function forEachElement(node: Node, cb: (el: Element) => void): void {
export function forEachElement(
node: Node,
cb: (el: Element) => void | false,
): 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)
const result = cb(next as Element)
if (result === false) {
break
}
} else if (isComment(next)) {
if (next.data === ']') {
if (--depth === 0) break