From e4c99402697374df6356f268529c987c7428bbed Mon Sep 17 00:00:00 2001 From: Julien Huang Date: Sat, 13 Apr 2024 11:06:09 +0200 Subject: [PATCH] fix: ssr improvement + static vnode rendering --- .../runtime/client-delayed-component.ts | 57 +++++++++++++------ 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index 9ab87f6e6d..30760ae440 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -1,39 +1,62 @@ -import { createStaticVNode, defineComponent, getCurrentInstance, h, onBeforeUnmount, onMounted, ref } from 'vue' +import { createStaticVNode, defineComponent, getCurrentInstance, h, onBeforeUnmount, onMounted, ref, createVNode } from 'vue' import type { Component, Ref, VNode } from 'vue' // import ClientOnly from '#app/components/client-only' import { useObserver } from '#app/utils' import { getFragmentHTML } from '#app/components/utils' import { useNuxtApp } from '#app/nuxt' +// todo find a better way to do it ? +function elementIsVisibleInViewport(el: Element) { + const { top, left, bottom, right } = el.getBoundingClientRect(); + const { innerHeight, innerWidth } = window; + return ((top > 0 && top < innerHeight) || + (bottom > 0 && bottom < innerHeight)) && + ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth)) +} + + /* @__NO_SIDE_EFFECTS__ */ export const createLazyIOClientPage = (componentLoader: Component) => { return defineComponent({ inheritAttrs: false, - setup (_, { attrs }) { + setup(_, { attrs }) { + + if (import.meta.server) { + return h('div', {}, [ + h(componentLoader, attrs) + ]) + } + const nuxt = useNuxtApp() const instance = getCurrentInstance()! - let vnode: VNode | null = null - if (import.meta.client && nuxt.isHydrating && instance.vnode?.el) { - vnode = createStaticVNode(getFragmentHTML(instance.vnode.el ?? null, true)?.join('') || '', 1) - } const isIntersecting = ref(false) const el: Ref = ref(null) let unobserve: (() => void) | null = null - onMounted(() => { - const observer = useObserver() - unobserve = observer!.observe(el.value as Element, () => { - isIntersecting.value = true - unobserve?.() - unobserve = null + + // todo can be refactored + if (instance.vnode.el && nuxt.isHydrating) { + isIntersecting.value = elementIsVisibleInViewport(instance.vnode.el as Element) + } + + if (!isIntersecting.value) { + onMounted(() => { + const observer = useObserver() + unobserve = observer!.observe(el.value as Element, () => { + isIntersecting.value = true + unobserve?.() + unobserve = null + }) }) - }) + } onBeforeUnmount(() => { unobserve?.() unobserve = null }) - return () => h('div', { ref: el }, [ - isIntersecting.value ? h(componentLoader, attrs) : vnode, - ]) + return () => { + return h('div', { ref: el }, [ + isIntersecting.value ? h(componentLoader, attrs) : (instance.vnode.el && nuxt.isHydrating) ? createVNode(createStaticVNode(getFragmentHTML(instance.vnode.el ?? null, true)?.join('') || '', 1)) : null, + ]) + } }, }) } @@ -42,7 +65,7 @@ export const createLazyIOClientPage = (componentLoader: Component) => { export const createLazyNetworkClientPage = (componentLoader: Component) => { return defineComponent({ inheritAttrs: false, - setup (_, { attrs }) { + setup(_, { attrs }) { const nuxt = useNuxtApp() const instance = getCurrentInstance()! let vnode: VNode | null = null