From e075dfad5c7649c6045e3711687ec888e7aa1a39 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Wed, 18 Sep 2024 10:44:23 +0300 Subject: [PATCH 01/14] perf(hydration): avoid observer if element is in viewport (#11639) --- .../runtime-core/src/hydrationStrategies.ts | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/packages/runtime-core/src/hydrationStrategies.ts b/packages/runtime-core/src/hydrationStrategies.ts index 51200fc1c..791ca9e52 100644 --- a/packages/runtime-core/src/hydrationStrategies.ts +++ b/packages/runtime-core/src/hydrationStrategies.ts @@ -26,6 +26,16 @@ export const hydrateOnIdle: HydrationStrategyFactory = 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 From 7257e6a34200409b3fc347d3bb807e11e2785974 Mon Sep 17 00:00:00 2001 From: linzhe <40790268+linzhe141@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:41:58 +0800 Subject: [PATCH 02/14] fix(hydration): avoid observing non-Element node (#11954) close #11952 --- packages/runtime-core/src/hydrationStrategies.ts | 1 + .../vue/__tests__/e2e/hydration-strat-visible.html | 8 +++++++- .../vue/__tests__/e2e/hydrationStrategies.spec.ts | 11 +++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/src/hydrationStrategies.ts b/packages/runtime-core/src/hydrationStrategies.ts index 791ca9e52..bb98ea93c 100644 --- a/packages/runtime-core/src/hydrationStrategies.ts +++ b/packages/runtime-core/src/hydrationStrategies.ts @@ -48,6 +48,7 @@ export const hydrateOnVisible: HydrationStrategyFactory< } }, opts) forEach(el => { + if (!(el instanceof Element)) return if (elementIsVisibleInViewport(el)) { hydrate() ob.disconnect() diff --git a/packages/vue/__tests__/e2e/hydration-strat-visible.html b/packages/vue/__tests__/e2e/hydration-strat-visible.html index 7b3b5ddf3..489222f86 100644 --- a/packages/vue/__tests__/e2e/hydration-strat-visible.html +++ b/packages/vue/__tests__/e2e/hydration-strat-visible.html @@ -11,9 +11,12 @@ + + `, + { inlineTemplate: true }, + ) + expect(content).toMatch(`_isRef(count) ? (count).value = $event : null`) + assertCode(content) + }) + test('v-model should not generate ref assignment code for non-setup bindings', () => { const { content } = compile( `