From 9f629ad45a4c31c2678182d90b634ee8807d19b1 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 24 Mar 2024 19:30:58 +0200 Subject: [PATCH 001/234] feat: lazy hydration --- packages/nuxt/src/components/runtime/client-io-component.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/nuxt/src/components/runtime/client-io-component.ts diff --git a/packages/nuxt/src/components/runtime/client-io-component.ts b/packages/nuxt/src/components/runtime/client-io-component.ts new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/packages/nuxt/src/components/runtime/client-io-component.ts @@ -0,0 +1 @@ + From d64d78fde8a01ed6cee05ad7d3a72cb5b6b39c96 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 24 Mar 2024 17:36:36 +0000 Subject: [PATCH 002/234] [autofix.ci] apply automated fixes --- packages/nuxt/src/components/runtime/client-io-component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/nuxt/src/components/runtime/client-io-component.ts b/packages/nuxt/src/components/runtime/client-io-component.ts index 8b13789179..e69de29bb2 100644 --- a/packages/nuxt/src/components/runtime/client-io-component.ts +++ b/packages/nuxt/src/components/runtime/client-io-component.ts @@ -1 +0,0 @@ - From 9ac1261662342fe544a9772ee8f88174ff344d8a Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 24 Mar 2024 19:41:29 +0200 Subject: [PATCH 003/234] feat: client-io-component.ts barebones functionality --- .../components/runtime/client-io-component.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/nuxt/src/components/runtime/client-io-component.ts b/packages/nuxt/src/components/runtime/client-io-component.ts index e69de29bb2..babcdc33eb 100644 --- a/packages/nuxt/src/components/runtime/client-io-component.ts +++ b/packages/nuxt/src/components/runtime/client-io-component.ts @@ -0,0 +1,24 @@ +import { defineComponent } from "vue" +export default defineComponent({ + setup() { + const data = ref(null); + const isIntersecting = ref(false); + const target = ref(null); + let observer: Ref = ref(null) + onMounted(() => { + const observer = new IntersectionObserver(entries => { + entries.forEach(entry => { + if (entry.isIntersecting) { + isIntersecting.value = true; + observer.unobserve(target.value); + } + }); + }); + observer.observe(target.value); + }); + + return { + isIntersecting + }; + } +}); From 84b0a71b8c2fb6b67457b5122e340e9c1999070d Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 24 Mar 2024 19:45:47 +0200 Subject: [PATCH 004/234] feat: provide an emit --- packages/nuxt/src/components/runtime/client-io-component.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/nuxt/src/components/runtime/client-io-component.ts b/packages/nuxt/src/components/runtime/client-io-component.ts index babcdc33eb..b1de3244a2 100644 --- a/packages/nuxt/src/components/runtime/client-io-component.ts +++ b/packages/nuxt/src/components/runtime/client-io-component.ts @@ -1,5 +1,6 @@ import { defineComponent } from "vue" export default defineComponent({ + emits: ['intersected'], setup() { const data = ref(null); const isIntersecting = ref(false); @@ -10,6 +11,7 @@ export default defineComponent({ entries.forEach(entry => { if (entry.isIntersecting) { isIntersecting.value = true; + emit('intersected'); observer.unobserve(target.value); } }); From ae2bb27ac0235f1effb8852f981532764374497f Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 24 Mar 2024 19:46:43 +0200 Subject: [PATCH 005/234] fix: provide ref --- packages/nuxt/src/components/runtime/client-io-component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nuxt/src/components/runtime/client-io-component.ts b/packages/nuxt/src/components/runtime/client-io-component.ts index b1de3244a2..6403e3e211 100644 --- a/packages/nuxt/src/components/runtime/client-io-component.ts +++ b/packages/nuxt/src/components/runtime/client-io-component.ts @@ -1,4 +1,4 @@ -import { defineComponent } from "vue" +import { defineComponent, ref } from "vue" export default defineComponent({ emits: ['intersected'], setup() { From 6a32dc1c9edbed4248b08e9513092025318bbc56 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 24 Mar 2024 19:52:16 +0200 Subject: [PATCH 006/234] fix: client-io-component.ts --- .../components/runtime/client-io-component.ts | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-io-component.ts b/packages/nuxt/src/components/runtime/client-io-component.ts index 6403e3e211..cb941d8aab 100644 --- a/packages/nuxt/src/components/runtime/client-io-component.ts +++ b/packages/nuxt/src/components/runtime/client-io-component.ts @@ -1,26 +1,32 @@ -import { defineComponent, ref } from "vue" +import { ref, onMounted, onUnmounted, defineComponent } from 'vue'; + export default defineComponent({ - emits: ['intersected'], - setup() { - const data = ref(null); - const isIntersecting = ref(false); - const target = ref(null); - let observer: Ref = ref(null) - onMounted(() => { - const observer = new IntersectionObserver(entries => { - entries.forEach(entry => { - if (entry.isIntersecting) { - isIntersecting.value = true; - emit('intersected'); - observer.unobserve(target.value); - } - }); + setup(props, { emit }) { + const intersectionTarget = ref(null); + let observer = null; + + const intersectionCallback = (entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + emit('intersect'); + observer.unobserve(entry.target); + } }); - observer.observe(target.value); + }; + + onMounted(() => { + observer = new IntersectionObserver(intersectionCallback); + observer.observe(intersectionTarget.value); + }); + + onUnmounted(() => { + if (observer) { + observer.disconnect(); + } }); return { - isIntersecting + intersectionTarget }; } }); From 932d1436880ee936891f4264e288ef19bd2f64e0 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 24 Mar 2024 17:54:31 +0000 Subject: [PATCH 007/234] [autofix.ci] apply automated fixes --- .../components/runtime/client-io-component.ts | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-io-component.ts b/packages/nuxt/src/components/runtime/client-io-component.ts index cb941d8aab..e8f07a47c7 100644 --- a/packages/nuxt/src/components/runtime/client-io-component.ts +++ b/packages/nuxt/src/components/runtime/client-io-component.ts @@ -1,32 +1,32 @@ -import { ref, onMounted, onUnmounted, defineComponent } from 'vue'; +import { defineComponent, onMounted, onUnmounted, ref } from 'vue' export default defineComponent({ - setup(props, { emit }) { - const intersectionTarget = ref(null); - let observer = null; + setup (props, { emit }) { + const intersectionTarget = ref(null) + let observer = null const intersectionCallback = (entries) => { - entries.forEach(entry => { + entries.forEach((entry) => { if (entry.isIntersecting) { - emit('intersect'); - observer.unobserve(entry.target); + emit('intersect') + observer.unobserve(entry.target) } - }); - }; + }) + } onMounted(() => { - observer = new IntersectionObserver(intersectionCallback); - observer.observe(intersectionTarget.value); - }); + observer = new IntersectionObserver(intersectionCallback) + observer.observe(intersectionTarget.value) + }) onUnmounted(() => { if (observer) { - observer.disconnect(); + observer.disconnect() } - }); + }) return { intersectionTarget - }; + } } -}); +}) From cb221ed57941f0a7fe8dab1ace634d30dc962d3b Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 24 Mar 2024 22:24:55 +0200 Subject: [PATCH 008/234] types: client-io-component.ts --- packages/nuxt/src/components/runtime/client-io-component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nuxt/src/components/runtime/client-io-component.ts b/packages/nuxt/src/components/runtime/client-io-component.ts index e8f07a47c7..081e2d82a7 100644 --- a/packages/nuxt/src/components/runtime/client-io-component.ts +++ b/packages/nuxt/src/components/runtime/client-io-component.ts @@ -3,7 +3,7 @@ import { defineComponent, onMounted, onUnmounted, ref } from 'vue' export default defineComponent({ setup (props, { emit }) { const intersectionTarget = ref(null) - let observer = null + let observer: IntersectionObserver | null = null const intersectionCallback = (entries) => { entries.forEach((entry) => { From 2ac2a975e05b533d1c037150d88b727fb44d7cc2 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 24 Mar 2024 23:08:51 +0200 Subject: [PATCH 009/234] fix: proper intersection callback type --- packages/nuxt/src/components/runtime/client-io-component.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-io-component.ts b/packages/nuxt/src/components/runtime/client-io-component.ts index 081e2d82a7..d7b1e7273b 100644 --- a/packages/nuxt/src/components/runtime/client-io-component.ts +++ b/packages/nuxt/src/components/runtime/client-io-component.ts @@ -2,10 +2,10 @@ import { defineComponent, onMounted, onUnmounted, ref } from 'vue' export default defineComponent({ setup (props, { emit }) { - const intersectionTarget = ref(null) + const intersectionTarget: Ref = ref(null) let observer: IntersectionObserver | null = null - const intersectionCallback = (entries) => { + const intersectionCallback: IntersectionObserverCallback = (entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { emit('intersect') @@ -16,7 +16,7 @@ export default defineComponent({ onMounted(() => { observer = new IntersectionObserver(intersectionCallback) - observer.observe(intersectionTarget.value) + observer.observe(intersectionTarget.value as Element) }) onUnmounted(() => { From d94436b9c4e13444059a1d09bf09188a217b89e4 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 24 Mar 2024 23:15:02 +0200 Subject: [PATCH 010/234] fix: import type Ref and provide emit --- packages/nuxt/src/components/runtime/client-io-component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/nuxt/src/components/runtime/client-io-component.ts b/packages/nuxt/src/components/runtime/client-io-component.ts index d7b1e7273b..f367d8252d 100644 --- a/packages/nuxt/src/components/runtime/client-io-component.ts +++ b/packages/nuxt/src/components/runtime/client-io-component.ts @@ -1,6 +1,8 @@ import { defineComponent, onMounted, onUnmounted, ref } from 'vue' +import type { Ref } from "vue" export default defineComponent({ + emits: ['intersect'], setup (props, { emit }) { const intersectionTarget: Ref = ref(null) let observer: IntersectionObserver | null = null @@ -9,7 +11,7 @@ export default defineComponent({ entries.forEach((entry) => { if (entry.isIntersecting) { emit('intersect') - observer.unobserve(entry.target) + observer!.unobserve(entry.target) } }) } From 85b4d6932bc18c5658b5cd855fcdd204b47e7812 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 24 Mar 2024 21:17:18 +0000 Subject: [PATCH 011/234] [autofix.ci] apply automated fixes --- packages/nuxt/src/components/runtime/client-io-component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nuxt/src/components/runtime/client-io-component.ts b/packages/nuxt/src/components/runtime/client-io-component.ts index f367d8252d..98b523e341 100644 --- a/packages/nuxt/src/components/runtime/client-io-component.ts +++ b/packages/nuxt/src/components/runtime/client-io-component.ts @@ -1,5 +1,5 @@ import { defineComponent, onMounted, onUnmounted, ref } from 'vue' -import type { Ref } from "vue" +import type { Ref } from 'vue' export default defineComponent({ emits: ['intersect'], From 624188a7cff39c976fffadc6309f10a72028ce8f Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 25 Mar 2024 15:06:53 +0200 Subject: [PATCH 012/234] feat: extract useObserver --- packages/nuxt/src/app/components/nuxt-link.ts | 44 +------------------ 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/packages/nuxt/src/app/components/nuxt-link.ts b/packages/nuxt/src/app/components/nuxt-link.ts index 659001884b..cbc0f5d206 100644 --- a/packages/nuxt/src/app/components/nuxt-link.ts +++ b/packages/nuxt/src/app/components/nuxt-link.ts @@ -14,6 +14,7 @@ import { onNuxtReady } from '../composables/ready' import { navigateTo, useRouter } from '../composables/router' import { useNuxtApp, useRuntimeConfig } from '../nuxt' import { cancelIdleCallback, requestIdleCallback } from '../compat/idle-callback' +import { useObserver } from "../utils" // @ts-expect-error virtual file import { nuxtLinkDefaults } from '#build/nuxt.config.mjs' @@ -403,49 +404,6 @@ function applyTrailingSlashBehavior (to: string, trailingSlash: NuxtLinkOptions[ } // --- Prefetching utils --- -type CallbackFn = () => void -type ObserveFn = (element: Element, callback: CallbackFn) => () => void - -function useObserver (): { observe: ObserveFn } | undefined { - if (import.meta.server) { return } - - const nuxtApp = useNuxtApp() - if (nuxtApp._observer) { - return nuxtApp._observer - } - - let observer: IntersectionObserver | null = null - - const callbacks = new Map() - - const observe: ObserveFn = (element, callback) => { - if (!observer) { - observer = new IntersectionObserver((entries) => { - for (const entry of entries) { - const callback = callbacks.get(entry.target) - const isVisible = entry.isIntersecting || entry.intersectionRatio > 0 - if (isVisible && callback) { callback() } - } - }) - } - callbacks.set(element, callback) - observer.observe(element) - return () => { - callbacks.delete(element) - observer!.unobserve(element) - if (callbacks.size === 0) { - observer!.disconnect() - observer = null - } - } - } - - const _observer = nuxtApp._observer = { - observe - } - - return _observer -} function isSlowConnection () { if (import.meta.server) { return } From f039dfd09f6ebf7618d346ab045a6bf1cf994029 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 25 Mar 2024 15:07:52 +0200 Subject: [PATCH 013/234] feat: provide exported function --- packages/nuxt/src/app/utils.ts | 61 ++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/packages/nuxt/src/app/utils.ts b/packages/nuxt/src/app/utils.ts index 4cc2040ad9..2dd929f23e 100644 --- a/packages/nuxt/src/app/utils.ts +++ b/packages/nuxt/src/app/utils.ts @@ -1,3 +1,64 @@ +import type { + AllowedComponentProps, + AnchorHTMLAttributes, + ComputedRef, + DefineComponent, + InjectionKey, PropType, + VNodeProps +} from 'vue' +import { computed, defineComponent, h, inject, onBeforeUnmount, onMounted, provide, ref, resolveComponent } from 'vue' +import type { RouteLocation, RouteLocationRaw, Router, RouterLinkProps } from '#vue-router' +import { hasProtocol, joinURL, parseQuery, parseURL, withTrailingSlash, withoutTrailingSlash } from 'ufo' +import { preloadRouteComponents } from './composables/preload' +import { onNuxtReady } from './composables/ready' +import { navigateTo, useRouter } from './composables/router' +import { useNuxtApp, useRuntimeConfig } from './nuxt' +import { cancelIdleCallback, requestIdleCallback } from './idle-callback' + export function toArray (value: T | T[]): T[] { return Array.isArray(value) ? value : [value] } + +export type CallbackFn = () => void +type ObserveFn = (element: Element, callback: CallbackFn) => () => void + +function useObserver (): { observe: ObserveFn } | undefined { + if (import.meta.server) { return } + + const nuxtApp = useNuxtApp() + if (nuxtApp._observer) { + return nuxtApp._observer + } + + let observer: IntersectionObserver | null = null + + const callbacks = new Map() + + const observe: ObserveFn = (element, callback) => { + if (!observer) { + observer = new IntersectionObserver((entries) => { + for (const entry of entries) { + const callback = callbacks.get(entry.target) + const isVisible = entry.isIntersecting || entry.intersectionRatio > 0 + if (isVisible && callback) { callback() } + } + }) + } + callbacks.set(element, callback) + observer.observe(element) + return () => { + callbacks.delete(element) + observer!.unobserve(element) + if (callbacks.size === 0) { + observer!.disconnect() + observer = null + } + } + } + + const _observer = nuxtApp._observer = { + observe + } + + return _observer +} From 7d2f3455112e7ae73473090e4a88354e42597a14 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 25 Mar 2024 15:12:09 +0200 Subject: [PATCH 014/234] fix: remove unnecessary imports --- packages/nuxt/src/app/utils.ts | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/nuxt/src/app/utils.ts b/packages/nuxt/src/app/utils.ts index 2dd929f23e..4d5d2c3ae8 100644 --- a/packages/nuxt/src/app/utils.ts +++ b/packages/nuxt/src/app/utils.ts @@ -1,19 +1,4 @@ -import type { - AllowedComponentProps, - AnchorHTMLAttributes, - ComputedRef, - DefineComponent, - InjectionKey, PropType, - VNodeProps -} from 'vue' -import { computed, defineComponent, h, inject, onBeforeUnmount, onMounted, provide, ref, resolveComponent } from 'vue' -import type { RouteLocation, RouteLocationRaw, Router, RouterLinkProps } from '#vue-router' -import { hasProtocol, joinURL, parseQuery, parseURL, withTrailingSlash, withoutTrailingSlash } from 'ufo' -import { preloadRouteComponents } from './composables/preload' -import { onNuxtReady } from './composables/ready' -import { navigateTo, useRouter } from './composables/router' -import { useNuxtApp, useRuntimeConfig } from './nuxt' -import { cancelIdleCallback, requestIdleCallback } from './idle-callback' +import { useNuxtApp } from './nuxt' export function toArray (value: T | T[]): T[] { return Array.isArray(value) ? value : [value] From f51c9362ce1c4681e05047b6f04152f3962c6e8e Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 25 Mar 2024 15:15:11 +0200 Subject: [PATCH 015/234] fix: export function as opposed to type --- packages/nuxt/src/app/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nuxt/src/app/utils.ts b/packages/nuxt/src/app/utils.ts index 4d5d2c3ae8..2bd768f257 100644 --- a/packages/nuxt/src/app/utils.ts +++ b/packages/nuxt/src/app/utils.ts @@ -4,10 +4,10 @@ export function toArray (value: T | T[]): T[] { return Array.isArray(value) ? value : [value] } -export type CallbackFn = () => void +type CallbackFn = () => void type ObserveFn = (element: Element, callback: CallbackFn) => () => void -function useObserver (): { observe: ObserveFn } | undefined { +export function useObserver (): { observe: ObserveFn } | undefined { if (import.meta.server) { return } const nuxtApp = useNuxtApp() From b3b16762022562ff38da11d5b98aa82f28a114e8 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:17:29 +0000 Subject: [PATCH 016/234] [autofix.ci] apply automated fixes --- packages/nuxt/src/app/components/nuxt-link.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nuxt/src/app/components/nuxt-link.ts b/packages/nuxt/src/app/components/nuxt-link.ts index cbc0f5d206..59adede3ad 100644 --- a/packages/nuxt/src/app/components/nuxt-link.ts +++ b/packages/nuxt/src/app/components/nuxt-link.ts @@ -14,7 +14,7 @@ import { onNuxtReady } from '../composables/ready' import { navigateTo, useRouter } from '../composables/router' import { useNuxtApp, useRuntimeConfig } from '../nuxt' import { cancelIdleCallback, requestIdleCallback } from '../compat/idle-callback' -import { useObserver } from "../utils" +import { useObserver } from '../utils' // @ts-expect-error virtual file import { nuxtLinkDefaults } from '#build/nuxt.config.mjs' From 966c17be720593d35d1d911b487eb8dc2141c481 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 25 Mar 2024 19:19:35 +0200 Subject: [PATCH 017/234] fix: provide a proper wrapper for IO with the comp --- .../components/runtime/client-io-component.ts | 66 ++++++++++--------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-io-component.ts b/packages/nuxt/src/components/runtime/client-io-component.ts index 98b523e341..0015a8804c 100644 --- a/packages/nuxt/src/components/runtime/client-io-component.ts +++ b/packages/nuxt/src/components/runtime/client-io-component.ts @@ -1,34 +1,38 @@ -import { defineComponent, onMounted, onUnmounted, ref } from 'vue' -import type { Ref } from 'vue' +import { defineComponent, h, ref, onMounted, onBeforeUnmount } from 'vue' +import type { Component, Ref } from "vue" +import ClientOnly from '#app/components/client-only' +import { useObserver } from "#app/utils" -export default defineComponent({ - emits: ['intersect'], - setup (props, { emit }) { - const intersectionTarget: Ref = ref(null) - let observer: IntersectionObserver | null = null - - const intersectionCallback: IntersectionObserverCallback = (entries) => { - entries.forEach((entry) => { - if (entry.isIntersecting) { - emit('intersect') - observer!.unobserve(entry.target) - } +export const createLazyIOClientPage = (componentLoader: Component) => { + return defineComponent({ + inheritAttrs: false, + setup (_, { attrs }) { + 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 + }) + }); + onBeforeUnmount(() => { + unobserve?.() + unobserve = null }) + return () => h('div', { ref: el }, [ + h(ClientOnly, undefined, { + default: () => { + if (isIntersecting.value) { + return h(componentLoader, attrs); + } else { + return null; + } + } + }) + ]); } - - onMounted(() => { - observer = new IntersectionObserver(intersectionCallback) - observer.observe(intersectionTarget.value as Element) - }) - - onUnmounted(() => { - if (observer) { - observer.disconnect() - } - }) - - return { - intersectionTarget - } - } -}) + }); +}; From f6ba2255e6fe8a268148156cf3df1585d66f7558 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 25 Mar 2024 19:21:01 +0200 Subject: [PATCH 018/234] fix: ensure observer is not undefined --- packages/nuxt/src/components/runtime/client-io-component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nuxt/src/components/runtime/client-io-component.ts b/packages/nuxt/src/components/runtime/client-io-component.ts index 0015a8804c..c3856b6747 100644 --- a/packages/nuxt/src/components/runtime/client-io-component.ts +++ b/packages/nuxt/src/components/runtime/client-io-component.ts @@ -12,7 +12,7 @@ export const createLazyIOClientPage = (componentLoader: Component) => { let unobserve: (() => void) | null = null onMounted(() => { const observer = useObserver() - unobserve = observer.observe(el.value as Element, () => { + unobserve = observer!.observe(el.value as Element, () => { isIntersecting.value = true unobserve?.() unobserve = null From 1e16b4a0886e93cd7b617d54a0661e445cc207d0 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 17:23:25 +0000 Subject: [PATCH 019/234] [autofix.ci] apply automated fixes --- .../components/runtime/client-io-component.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-io-component.ts b/packages/nuxt/src/components/runtime/client-io-component.ts index c3856b6747..dab60611f3 100644 --- a/packages/nuxt/src/components/runtime/client-io-component.ts +++ b/packages/nuxt/src/components/runtime/client-io-component.ts @@ -1,14 +1,14 @@ -import { defineComponent, h, ref, onMounted, onBeforeUnmount } from 'vue' -import type { Component, Ref } from "vue" +import { defineComponent, h, onBeforeUnmount, onMounted, ref } from 'vue' +import type { Component, Ref } from 'vue' import ClientOnly from '#app/components/client-only' -import { useObserver } from "#app/utils" +import { useObserver } from '#app/utils' export const createLazyIOClientPage = (componentLoader: Component) => { return defineComponent({ inheritAttrs: false, setup (_, { attrs }) { - const isIntersecting = ref(false); - const el: Ref = ref(null); + const isIntersecting = ref(false) + const el: Ref = ref(null) let unobserve: (() => void) | null = null onMounted(() => { const observer = useObserver() @@ -17,7 +17,7 @@ export const createLazyIOClientPage = (componentLoader: Component) => { unobserve?.() unobserve = null }) - }); + }) onBeforeUnmount(() => { unobserve?.() unobserve = null @@ -26,13 +26,13 @@ export const createLazyIOClientPage = (componentLoader: Component) => { h(ClientOnly, undefined, { default: () => { if (isIntersecting.value) { - return h(componentLoader, attrs); + return h(componentLoader, attrs) } else { - return null; + return null } } }) - ]); + ]) } - }); -}; + }) +} From 600f55d2a35fc3d1d44baf5bf48d152558c9aca1 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Tue, 26 Mar 2024 16:20:49 +0200 Subject: [PATCH 020/234] wip: rename client-io-component.ts to client-delayed-component.ts for future wrappers --- .../{client-io-component.ts => client-delayed-component.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/nuxt/src/components/runtime/{client-io-component.ts => client-delayed-component.ts} (100%) diff --git a/packages/nuxt/src/components/runtime/client-io-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts similarity index 100% rename from packages/nuxt/src/components/runtime/client-io-component.ts rename to packages/nuxt/src/components/runtime/client-delayed-component.ts From 66938bc152bf911d7a039cfb4fe41b0dfe7ad997 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 07:24:28 +0000 Subject: [PATCH 021/234] [autofix.ci] apply automated fixes --- packages/nuxt/src/app/utils.ts | 2 +- .../nuxt/src/components/runtime/client-delayed-component.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/nuxt/src/app/utils.ts b/packages/nuxt/src/app/utils.ts index 2bd768f257..a46715cb21 100644 --- a/packages/nuxt/src/app/utils.ts +++ b/packages/nuxt/src/app/utils.ts @@ -42,7 +42,7 @@ export function useObserver (): { observe: ObserveFn } | undefined { } const _observer = nuxtApp._observer = { - observe + observe, } return _observer diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index dab60611f3..e50ef9849a 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -30,9 +30,9 @@ export const createLazyIOClientPage = (componentLoader: Component) => { } else { return null } - } - }) + }, + }), ]) - } + }, }) } From 20a32b2200b0ec631af8021332e996da57eebccd Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 8 Apr 2024 17:19:59 +0300 Subject: [PATCH 022/234] wip: provide hardcoded check to test delayed hydration runtime comp --- packages/nuxt/src/components/loader.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/nuxt/src/components/loader.ts b/packages/nuxt/src/components/loader.ts index 628638ffad..8c74efd0e1 100644 --- a/packages/nuxt/src/components/loader.ts +++ b/packages/nuxt/src/components/loader.ts @@ -21,7 +21,7 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { const exclude = options.transform?.exclude || [] const include = options.transform?.include || [] const serverComponentRuntime = resolve(distDir, 'components/runtime/server-component') - + const clientDelayedComponentRuntime = resolve(distDir, 'components/runtime/client-delayed-component') return { name: 'nuxt:components-loader', enforce: 'post', @@ -72,9 +72,17 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { } if (lazy) { - imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) - identifier += '_lazy' - imports.add(`const ${identifier} = __defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)${isClientOnly ? '.then(c => createClientOnly(c))' : ''})`) + // Temporary hardcoded check to verify runtime functionality + if (name === "DelayedWrapperTestComponent") { + imports.add(genImport(clientDelayedComponentRuntime, [{ name: 'createLazyIOClientPage' }])) + imports.add(`const ${identifier} = createLazyIOClientPage(${JSON.stringify(name)})`) + identifier += '_delayedIO' + } + else { + imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) + identifier += '_lazy' + imports.add(`const ${identifier} = __defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)${isClientOnly ? '.then(c => createClientOnly(c))' : ''})`) + } } else { imports.add(genImport(component.filePath, [{ name: component._raw ? 'default' : component.export, as: identifier }])) From 816ba111b4caa2e043bedb730d0befb7b32cda9a Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:22:13 +0000 Subject: [PATCH 023/234] [autofix.ci] apply automated fixes --- packages/nuxt/src/components/loader.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/nuxt/src/components/loader.ts b/packages/nuxt/src/components/loader.ts index 8c74efd0e1..00f3139aa7 100644 --- a/packages/nuxt/src/components/loader.ts +++ b/packages/nuxt/src/components/loader.ts @@ -73,12 +73,11 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { if (lazy) { // Temporary hardcoded check to verify runtime functionality - if (name === "DelayedWrapperTestComponent") { - imports.add(genImport(clientDelayedComponentRuntime, [{ name: 'createLazyIOClientPage' }])) - imports.add(`const ${identifier} = createLazyIOClientPage(${JSON.stringify(name)})`) - identifier += '_delayedIO' - } - else { + if (name === 'DelayedWrapperTestComponent') { + imports.add(genImport(clientDelayedComponentRuntime, [{ name: 'createLazyIOClientPage' }])) + imports.add(`const ${identifier} = createLazyIOClientPage(${JSON.stringify(name)})`) + identifier += '_delayedIO' + } else { imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) identifier += '_lazy' imports.add(`const ${identifier} = __defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)${isClientOnly ? '.then(c => createClientOnly(c))' : ''})`) From 6c683e0b6eaeca01e2158c2bf77a31dc7e7c4f20 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 8 Apr 2024 17:37:18 +0300 Subject: [PATCH 024/234] wip: add comp to lazy-import-components --- test/fixtures/basic/pages/lazy-import-components/index.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/fixtures/basic/pages/lazy-import-components/index.vue b/test/fixtures/basic/pages/lazy-import-components/index.vue index 0f46fc9dfc..e57695045f 100644 --- a/test/fixtures/basic/pages/lazy-import-components/index.vue +++ b/test/fixtures/basic/pages/lazy-import-components/index.vue @@ -3,5 +3,7 @@ +
This is a very tall div
+ This shouldn't be visible at first! From 998c1ba5513826019769ee6cc3451467e50411f7 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:39:42 +0000 Subject: [PATCH 025/234] [autofix.ci] apply automated fixes --- test/fixtures/basic/pages/lazy-import-components/index.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/fixtures/basic/pages/lazy-import-components/index.vue b/test/fixtures/basic/pages/lazy-import-components/index.vue index e57695045f..1760bbd4fb 100644 --- a/test/fixtures/basic/pages/lazy-import-components/index.vue +++ b/test/fixtures/basic/pages/lazy-import-components/index.vue @@ -3,7 +3,9 @@ -
This is a very tall div
+
+ This is a very tall div +
This shouldn't be visible at first! From 4cc9efe563598339453988db236e09bfeb89d6b4 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 8 Apr 2024 17:59:41 +0300 Subject: [PATCH 026/234] wip: add initial lazy load check --- test/basic.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/basic.test.ts b/test/basic.test.ts index 9e5983c4c4..2bd13d73f0 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2562,4 +2562,8 @@ describe('lazy import components', () => { it('lazy load named component with mode server', () => { expect(html).toContain('lazy-named-comp-server') }) + + it('lazy load delayed hydration comps at the right time', () => { + expect(html).not.toContain("This shouldn't be visible at first!") + }) }) From d209a02492cc6feacb85f262abdeaa925d2873dc Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 15:02:03 +0000 Subject: [PATCH 027/234] [autofix.ci] apply automated fixes --- test/basic.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 2bd13d73f0..b0c7787d6d 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2562,8 +2562,8 @@ describe('lazy import components', () => { it('lazy load named component with mode server', () => { expect(html).toContain('lazy-named-comp-server') }) - + it('lazy load delayed hydration comps at the right time', () => { - expect(html).not.toContain("This shouldn't be visible at first!") + expect(html).not.toContain('This shouldn\'t be visible at first!') }) }) From 88bae15ad7ed1e5c1e0c3ec6607931baea9160d9 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 8 Apr 2024 18:52:27 +0300 Subject: [PATCH 028/234] wip: testing page text getting --- test/basic.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/basic.test.ts b/test/basic.test.ts index b0c7787d6d..170e7dd73a 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2565,5 +2565,7 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', () => { expect(html).not.toContain('This shouldn\'t be visible at first!') + const { page } = await renderPage('/lazy-import-components') + expect(page.locator('body').getByText('This shouldn\'t be visible at first!')).not.toBeDefined() }) }) From 00a68bd7ff3951095d3fd1cc557da89fbbe14558 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 8 Apr 2024 18:56:25 +0300 Subject: [PATCH 029/234] fix: async --- test/basic.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 170e7dd73a..d7ea4053e1 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2563,7 +2563,7 @@ describe('lazy import components', () => { expect(html).toContain('lazy-named-comp-server') }) - it('lazy load delayed hydration comps at the right time', () => { + it('lazy load delayed hydration comps at the right time', async () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') expect(page.locator('body').getByText('This shouldn\'t be visible at first!')).not.toBeDefined() From 10f7f22c828f4189d9c2c403fac8836df06d1dfd Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 8 Apr 2024 19:03:15 +0300 Subject: [PATCH 030/234] fix: check for count --- test/basic.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index d7ea4053e1..c4eed12891 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2566,6 +2566,6 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', async () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') - expect(page.locator('body').getByText('This shouldn\'t be visible at first!')).not.toBeDefined() + expect(page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(0) }) }) From 84b2333cf567014809edc83390734c7938de9bf7 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 8 Apr 2024 19:09:35 +0300 Subject: [PATCH 031/234] fix: await --- test/basic.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index c4eed12891..a5a42fca10 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2566,6 +2566,6 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', async () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') - expect(page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(0) + expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(0) }) }) From d89e70e065dadb6157a4a78b82e1378e6f9b698a Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 8 Apr 2024 19:18:09 +0300 Subject: [PATCH 032/234] Create DelayedWrapperTestComponent.vue --- .../basic/components/DelayedWrapperTestComponent.vue | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/fixtures/basic/components/DelayedWrapperTestComponent.vue diff --git a/test/fixtures/basic/components/DelayedWrapperTestComponent.vue b/test/fixtures/basic/components/DelayedWrapperTestComponent.vue new file mode 100644 index 0000000000..d42e68faf4 --- /dev/null +++ b/test/fixtures/basic/components/DelayedWrapperTestComponent.vue @@ -0,0 +1,5 @@ + From 4b0d88c54e6e794b1badb7f159918305954ea457 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 8 Apr 2024 19:18:47 +0300 Subject: [PATCH 033/234] refactor: use a component to test transform --- test/fixtures/basic/pages/lazy-import-components/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/basic/pages/lazy-import-components/index.vue b/test/fixtures/basic/pages/lazy-import-components/index.vue index 1760bbd4fb..c1cd4ce9f6 100644 --- a/test/fixtures/basic/pages/lazy-import-components/index.vue +++ b/test/fixtures/basic/pages/lazy-import-components/index.vue @@ -6,6 +6,6 @@
This is a very tall div
- This shouldn't be visible at first! + From 2236f7856621068c5feb0acd09f74b95b61c25b5 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 16:21:16 +0000 Subject: [PATCH 034/234] [autofix.ci] apply automated fixes --- test/fixtures/basic/pages/lazy-import-components/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/basic/pages/lazy-import-components/index.vue b/test/fixtures/basic/pages/lazy-import-components/index.vue index c1cd4ce9f6..eafa6d8375 100644 --- a/test/fixtures/basic/pages/lazy-import-components/index.vue +++ b/test/fixtures/basic/pages/lazy-import-components/index.vue @@ -6,6 +6,6 @@
This is a very tall div
- + From 36dc73152f69e84fcd76347eec6d6bd77f88aedf Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 8 Apr 2024 19:28:58 +0300 Subject: [PATCH 035/234] fix: append identifier before import --- packages/nuxt/src/components/loader.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nuxt/src/components/loader.ts b/packages/nuxt/src/components/loader.ts index 00f3139aa7..129a229bc0 100644 --- a/packages/nuxt/src/components/loader.ts +++ b/packages/nuxt/src/components/loader.ts @@ -75,8 +75,8 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { // Temporary hardcoded check to verify runtime functionality if (name === 'DelayedWrapperTestComponent') { imports.add(genImport(clientDelayedComponentRuntime, [{ name: 'createLazyIOClientPage' }])) - imports.add(`const ${identifier} = createLazyIOClientPage(${JSON.stringify(name)})`) identifier += '_delayedIO' + imports.add(`const ${identifier} = createLazyIOClientPage(${JSON.stringify(name)})`) } else { imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) identifier += '_lazy' From dd36a181401b660349b7d2a52243701928cc0f63 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 8 Apr 2024 19:58:33 +0300 Subject: [PATCH 036/234] wip: add scrolling to test for delayed hydration --- test/basic.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/basic.test.ts b/test/basic.test.ts index a5a42fca10..fef26e99f3 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2567,5 +2567,9 @@ describe('lazy import components', () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(0) + await page.evaluate(()=>{ + window.scrollTo(0, document.body.scrollHeight) + }) + expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(1) }) }) From bd7ca85fb4e03c83e474fc36956b50e4ce4d911d Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 17:00:52 +0000 Subject: [PATCH 037/234] [autofix.ci] apply automated fixes --- test/basic.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index fef26e99f3..8757d8cca1 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2567,9 +2567,9 @@ describe('lazy import components', () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(0) - await page.evaluate(()=>{ - window.scrollTo(0, document.body.scrollHeight) - }) + await page.evaluate(() => { + window.scrollTo(0, document.body.scrollHeight) + }) expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(1) }) }) From 5653d9e255780ae5a8c5cfd6607f361710e85a02 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 8 Apr 2024 20:16:18 +0300 Subject: [PATCH 038/234] wip: retrying through mouse.wheel --- test/basic.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 8757d8cca1..36ea3c7197 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2567,9 +2567,7 @@ describe('lazy import components', () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(0) - await page.evaluate(() => { - window.scrollTo(0, document.body.scrollHeight) - }) + await page.mouse.wheel(0,4000) expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(1) }) }) From 26f2e4e7d1b9cbb6bb836d040666b4552c0d2146 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 17:18:37 +0000 Subject: [PATCH 039/234] [autofix.ci] apply automated fixes --- test/basic.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 36ea3c7197..a7d010d04b 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2567,7 +2567,7 @@ describe('lazy import components', () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(0) - await page.mouse.wheel(0,4000) + await page.mouse.wheel(0, 4000) expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(1) }) }) From 988a99b771a64d30b7cf4e411bea26690d0822fe Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Tue, 9 Apr 2024 21:49:02 +0300 Subject: [PATCH 040/234] fix: don't use the default that is used for slots --- .../components/runtime/client-delayed-component.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index e50ef9849a..a7b9595eaf 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -23,15 +23,9 @@ export const createLazyIOClientPage = (componentLoader: Component) => { unobserve = null }) return () => h('div', { ref: el }, [ - h(ClientOnly, undefined, { - default: () => { - if (isIntersecting.value) { - return h(componentLoader, attrs) - } else { - return null - } - }, - }), + h(ClientOnly, undefined, [ + isIntersecting.value ? h(componentLoader, attrs) : null + ]), ]) }, }) From 669fcee81bfa33b0c68cb28ae91c960ab18d8283 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 9 Apr 2024 18:51:50 +0000 Subject: [PATCH 041/234] [autofix.ci] apply automated fixes --- .../nuxt/src/components/runtime/client-delayed-component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index a7b9595eaf..0a538df3b7 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -24,7 +24,7 @@ export const createLazyIOClientPage = (componentLoader: Component) => { }) return () => h('div', { ref: el }, [ h(ClientOnly, undefined, [ - isIntersecting.value ? h(componentLoader, attrs) : null + isIntersecting.value ? h(componentLoader, attrs) : null, ]), ]) }, From 9b94d100a2e46aab8986c531317a236ddfc32280 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Tue, 9 Apr 2024 22:01:37 +0300 Subject: [PATCH 042/234] chore: retrying without ClientOnly --- .../nuxt/src/components/runtime/client-delayed-component.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index 0a538df3b7..60c1e6a5b9 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -1,6 +1,6 @@ import { defineComponent, h, onBeforeUnmount, onMounted, ref } from 'vue' import type { Component, Ref } from 'vue' -import ClientOnly from '#app/components/client-only' +// import ClientOnly from '#app/components/client-only' import { useObserver } from '#app/utils' export const createLazyIOClientPage = (componentLoader: Component) => { @@ -23,9 +23,7 @@ export const createLazyIOClientPage = (componentLoader: Component) => { unobserve = null }) return () => h('div', { ref: el }, [ - h(ClientOnly, undefined, [ - isIntersecting.value ? h(componentLoader, attrs) : null, - ]), + isIntersecting.value ? h(componentLoader, attrs) : null, ]) }, }) From 59edd16fce71078c525ec3479598271dd9ab87cb Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Tue, 9 Apr 2024 22:12:57 +0300 Subject: [PATCH 044/234] chore: mark no side effects --- packages/nuxt/src/components/runtime/client-delayed-component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index 60c1e6a5b9..a0b115edf9 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -3,6 +3,7 @@ import type { Component, Ref } from 'vue' // import ClientOnly from '#app/components/client-only' import { useObserver } from '#app/utils' +/* @__NO_SIDE_EFFECTS__ */ export const createLazyIOClientPage = (componentLoader: Component) => { return defineComponent({ inheritAttrs: false, From d4bca6cd5769908bb9e325d224a316e9415dbc5c Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Tue, 9 Apr 2024 22:24:48 +0300 Subject: [PATCH 045/234] refactor: use page.evaluate --- test/basic.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index a7d010d04b..5de13d028b 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2567,7 +2567,7 @@ describe('lazy import components', () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(0) - await page.mouse.wheel(0, 4000) + await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(1) }) }) From b3250e4a46814403f40d21bb76ff81fcf1a747b5 Mon Sep 17 00:00:00 2001 From: julien huang Date: Tue, 9 Apr 2024 21:55:27 +0200 Subject: [PATCH 046/234] fix(nuxt): send the component loader and not the name --- packages/nuxt/src/components/loader.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/nuxt/src/components/loader.ts b/packages/nuxt/src/components/loader.ts index 129a229bc0..fa92433bf0 100644 --- a/packages/nuxt/src/components/loader.ts +++ b/packages/nuxt/src/components/loader.ts @@ -74,9 +74,10 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { if (lazy) { // Temporary hardcoded check to verify runtime functionality if (name === 'DelayedWrapperTestComponent') { + imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) imports.add(genImport(clientDelayedComponentRuntime, [{ name: 'createLazyIOClientPage' }])) identifier += '_delayedIO' - imports.add(`const ${identifier} = createLazyIOClientPage(${JSON.stringify(name)})`) + imports.add(`const ${identifier} = createLazyIOClientPage(__defineAsyncComponent(${genDynamicImport(component.filePath)}))`) } else { imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) identifier += '_lazy' From 8c1c23a422a78514b5a5bb8f2174d4495a9b64f7 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Tue, 9 Apr 2024 23:47:41 +0300 Subject: [PATCH 047/234] chore: trying to resolve default --- packages/nuxt/src/components/loader.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nuxt/src/components/loader.ts b/packages/nuxt/src/components/loader.ts index fa92433bf0..46aba77117 100644 --- a/packages/nuxt/src/components/loader.ts +++ b/packages/nuxt/src/components/loader.ts @@ -77,7 +77,7 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) imports.add(genImport(clientDelayedComponentRuntime, [{ name: 'createLazyIOClientPage' }])) identifier += '_delayedIO' - imports.add(`const ${identifier} = createLazyIOClientPage(__defineAsyncComponent(${genDynamicImport(component.filePath)}))`) + imports.add(`const ${identifier} = createLazyIOClientPage(__defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)))`) } else { imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) identifier += '_lazy' From 45e49c5beb96ecf2f07462846dd7204910637dd8 Mon Sep 17 00:00:00 2001 From: julien huang Date: Tue, 9 Apr 2024 23:10:31 +0200 Subject: [PATCH 048/234] test: wait for network idle --- test/basic.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 5de13d028b..a3597b8dbc 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2544,7 +2544,7 @@ describe('import components', () => { }) }) -describe('lazy import components', () => { +describe.only('lazy import components', () => { let html = '' it.sequential('fetch lazy-import-components page', async () => { @@ -2568,6 +2568,7 @@ describe('lazy import components', () => { const { page } = await renderPage('/lazy-import-components') expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(0) await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) + await page.waitForLoadState('networkidle') expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(1) }) }) From 382bc93efea8bb625e38862b5d54bc5112d7558d Mon Sep 17 00:00:00 2001 From: julien huang Date: Tue, 9 Apr 2024 23:14:44 +0200 Subject: [PATCH 049/234] test: remove only --- test/basic.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index a3597b8dbc..9b9046902a 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2544,7 +2544,7 @@ describe('import components', () => { }) }) -describe.only('lazy import components', () => { +describe('lazy import components', () => { let html = '' it.sequential('fetch lazy-import-components page', async () => { From 503b560d9b3264a70dc2b550bd3f1b5f5f7d9ce5 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Wed, 10 Apr 2024 10:00:38 +0300 Subject: [PATCH 050/234] wip: network idle based delayed component --- .../runtime/client-delayed-component.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index a0b115edf9..af37ff6b05 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -29,3 +29,28 @@ export const createLazyIOClientPage = (componentLoader: Component) => { }, }) } + +/* @__NO_SIDE_EFFECTS__ */ +export const createLazyNetworkClientPage = (componentLoader: Component) => { + return defineComponent({ + inheritAttrs: false, + setup (_, { attrs }) { + const isIdle = ref(false) + let idleHandle: number | null = null + onMounted(() => { + idleHandle = requestIdleCallback(() => { + isIdle.value = true + cancelIdleCallback(idleHandle) + idleHandle = null + }) + }) + onBeforeUnmount(() => { + if (idleHandle) { + cancelIdleCallback(idleHandle) + idleHandle = null + } + }) + return () => isIdle.value ? h(componentLoader, attrs) : null, + }, + }) +} From 2df20ac6813b18e3b28cab7addbfb546cd864969 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Wed, 10 Apr 2024 10:02:39 +0300 Subject: [PATCH 051/234] fix: remove comma --- .../nuxt/src/components/runtime/client-delayed-component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index af37ff6b05..95f0799634 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -50,7 +50,7 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { idleHandle = null } }) - return () => isIdle.value ? h(componentLoader, attrs) : null, + return () => isIdle.value ? h(componentLoader, attrs) : null }, }) } From effebbb02f91429eba13f98942a58dc404836aac Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Wed, 10 Apr 2024 10:32:26 +0300 Subject: [PATCH 052/234] fix: strict types --- .../nuxt/src/components/runtime/client-delayed-component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index 95f0799634..c14f92d412 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -40,13 +40,13 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { onMounted(() => { idleHandle = requestIdleCallback(() => { isIdle.value = true - cancelIdleCallback(idleHandle) + cancelIdleCallback(idleHandle as unknown as number) idleHandle = null }) }) onBeforeUnmount(() => { if (idleHandle) { - cancelIdleCallback(idleHandle) + cancelIdleCallback(idleHandle as unknown as number) idleHandle = null } }) From 2375650c36e7e38c83b018a650af1d8575919fb3 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Thu, 11 Apr 2024 00:02:48 +0300 Subject: [PATCH 053/234] feat: support lazy hydration on SSR --- .../runtime/client-delayed-component.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index c14f92d412..c899bd3e1d 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -1,13 +1,20 @@ -import { defineComponent, h, onBeforeUnmount, onMounted, ref } from 'vue' +import { defineComponent, h, onBeforeUnmount, onMounted, ref, getCurrentInstance, createStaticVNode } from 'vue' import type { Component, Ref } from 'vue' // import ClientOnly from '#app/components/client-only' import { useObserver } from '#app/utils' +import { getFragmentHTML } from '#app/components/utils' /* @__NO_SIDE_EFFECTS__ */ export const createLazyIOClientPage = (componentLoader: Component) => { return defineComponent({ inheritAttrs: false, setup (_, { attrs }) { + const nuxt = useNuxtApp() + const instance = getCurrentInstance()! + let vnode = null + if (import.meta.client && nuxt.isHydrating) { + vnode = createStaticVNode(getFragmentHTML(instance.vnode.el), 1) + } const isIntersecting = ref(false) const el: Ref = ref(null) let unobserve: (() => void) | null = null @@ -24,7 +31,7 @@ export const createLazyIOClientPage = (componentLoader: Component) => { unobserve = null }) return () => h('div', { ref: el }, [ - isIntersecting.value ? h(componentLoader, attrs) : null, + isIntersecting.value ? h(componentLoader, attrs) : vnode, ]) }, }) @@ -35,6 +42,12 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { return defineComponent({ inheritAttrs: false, setup (_, { attrs }) { + const nuxt = useNuxtApp() + const instance = getCurrentInstance()! + let vnode = null + if (import.meta.client && nuxt.isHydrating) { + vnode = createStaticVNode(getFragmentHTML(instance.vnode.el), 1) + } const isIdle = ref(false) let idleHandle: number | null = null onMounted(() => { @@ -50,7 +63,7 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { idleHandle = null } }) - return () => isIdle.value ? h(componentLoader, attrs) : null + return () => isIdle.value ? h(componentLoader, attrs) : vnode }, }) } From 31002ecc2d832f439a332c82cb6e099601304435 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 10 Apr 2024 21:05:11 +0000 Subject: [PATCH 054/234] [autofix.ci] apply automated fixes --- .../nuxt/src/components/runtime/client-delayed-component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index c899bd3e1d..24a6c5d65e 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -1,4 +1,4 @@ -import { defineComponent, h, onBeforeUnmount, onMounted, ref, getCurrentInstance, createStaticVNode } from 'vue' +import { createStaticVNode, defineComponent, getCurrentInstance, h, onBeforeUnmount, onMounted, ref } from 'vue' import type { Component, Ref } from 'vue' // import ClientOnly from '#app/components/client-only' import { useObserver } from '#app/utils' From 580d9bd4635a557c4993557333d05c942f917958 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Thu, 11 Apr 2024 00:06:49 +0300 Subject: [PATCH 055/234] fix: import useNuxtApp and provide types --- .../nuxt/src/components/runtime/client-delayed-component.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index 24a6c5d65e..5ce547db56 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -3,6 +3,7 @@ import type { Component, Ref } 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' /* @__NO_SIDE_EFFECTS__ */ export const createLazyIOClientPage = (componentLoader: Component) => { @@ -11,7 +12,7 @@ export const createLazyIOClientPage = (componentLoader: Component) => { setup (_, { attrs }) { const nuxt = useNuxtApp() const instance = getCurrentInstance()! - let vnode = null + let vnode: string | null = null if (import.meta.client && nuxt.isHydrating) { vnode = createStaticVNode(getFragmentHTML(instance.vnode.el), 1) } @@ -44,7 +45,7 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { setup (_, { attrs }) { const nuxt = useNuxtApp() const instance = getCurrentInstance()! - let vnode = null + let vnode: string | null = null if (import.meta.client && nuxt.isHydrating) { vnode = createStaticVNode(getFragmentHTML(instance.vnode.el), 1) } From e2d0350698afdd7e939f94f0597731501259e010 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Thu, 11 Apr 2024 00:10:54 +0300 Subject: [PATCH 056/234] fix: use VNode type --- .../nuxt/src/components/runtime/client-delayed-component.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index 5ce547db56..1acf7daac3 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -1,5 +1,5 @@ import { createStaticVNode, defineComponent, getCurrentInstance, h, onBeforeUnmount, onMounted, ref } from 'vue' -import type { Component, Ref } 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' @@ -12,7 +12,7 @@ export const createLazyIOClientPage = (componentLoader: Component) => { setup (_, { attrs }) { const nuxt = useNuxtApp() const instance = getCurrentInstance()! - let vnode: string | null = null + let vnode: VNode | null = null if (import.meta.client && nuxt.isHydrating) { vnode = createStaticVNode(getFragmentHTML(instance.vnode.el), 1) } @@ -45,7 +45,7 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { setup (_, { attrs }) { const nuxt = useNuxtApp() const instance = getCurrentInstance()! - let vnode: string | null = null + let vnode: VNode | null = null if (import.meta.client && nuxt.isHydrating) { vnode = createStaticVNode(getFragmentHTML(instance.vnode.el), 1) } From dc6d92277336c584b9f22c228ab94231b9adb328 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Thu, 11 Apr 2024 00:13:22 +0300 Subject: [PATCH 057/234] fix: join the fragments --- .../nuxt/src/components/runtime/client-delayed-component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index 1acf7daac3..0009962480 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -14,7 +14,7 @@ export const createLazyIOClientPage = (componentLoader: Component) => { const instance = getCurrentInstance()! let vnode: VNode | null = null if (import.meta.client && nuxt.isHydrating) { - vnode = createStaticVNode(getFragmentHTML(instance.vnode.el), 1) + vnode = createStaticVNode(getFragmentHTML(instance.vnode.el).join(''), 1) } const isIntersecting = ref(false) const el: Ref = ref(null) @@ -47,7 +47,7 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { const instance = getCurrentInstance()! let vnode: VNode | null = null if (import.meta.client && nuxt.isHydrating) { - vnode = createStaticVNode(getFragmentHTML(instance.vnode.el), 1) + vnode = createStaticVNode(getFragmentHTML(instance.vnode.el).join(''), 1) } const isIdle = ref(false) let idleHandle: number | null = null From 2ee3cf53eebbfc31ab4def9882d03854efd10125 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Thu, 11 Apr 2024 00:16:39 +0300 Subject: [PATCH 058/234] fix: verify none null --- .../src/components/runtime/client-delayed-component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index 0009962480..177819c34f 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -13,8 +13,8 @@ export const createLazyIOClientPage = (componentLoader: Component) => { const nuxt = useNuxtApp() const instance = getCurrentInstance()! let vnode: VNode | null = null - if (import.meta.client && nuxt.isHydrating) { - vnode = createStaticVNode(getFragmentHTML(instance.vnode.el).join(''), 1) + if (import.meta.client && nuxt.isHydrating && instance.vnode?.el) { + vnode = createStaticVNode(getFragmentHTML(instance.vnode.el).join('') || '', 1) } const isIntersecting = ref(false) const el: Ref = ref(null) @@ -46,8 +46,8 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { const nuxt = useNuxtApp() const instance = getCurrentInstance()! let vnode: VNode | null = null - if (import.meta.client && nuxt.isHydrating) { - vnode = createStaticVNode(getFragmentHTML(instance.vnode.el).join(''), 1) + if (import.meta.client && nuxt.isHydrating && instance.vnode?.el) { + vnode = createStaticVNode(getFragmentHTML(instance.vnode.el).join('') || '', 1) } const isIdle = ref(false) let idleHandle: number | null = null From 8555cec97d27eb6c27a2c1f2182d3d5e316cb290 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Thu, 11 Apr 2024 00:20:34 +0300 Subject: [PATCH 059/234] fix: ensure el itself isn't null --- .../nuxt/src/components/runtime/client-delayed-component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index 177819c34f..c2751450a5 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -14,7 +14,7 @@ export const createLazyIOClientPage = (componentLoader: Component) => { const instance = getCurrentInstance()! let vnode: VNode | null = null if (import.meta.client && nuxt.isHydrating && instance.vnode?.el) { - vnode = createStaticVNode(getFragmentHTML(instance.vnode.el).join('') || '', 1) + vnode = createStaticVNode(getFragmentHTML(instance.vnode.el ?? null, true).join('') || '', 1) } const isIntersecting = ref(false) const el: Ref = ref(null) @@ -47,7 +47,7 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { const instance = getCurrentInstance()! let vnode: VNode | null = null if (import.meta.client && nuxt.isHydrating && instance.vnode?.el) { - vnode = createStaticVNode(getFragmentHTML(instance.vnode.el).join('') || '', 1) + vnode = createStaticVNode(getFragmentHTML(instance.vnode.el ?? null, true).join('') || '', 1) } const isIdle = ref(false) let idleHandle: number | null = null From cfb6660fcd17dacb496a32f213751a1b316fb409 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Thu, 11 Apr 2024 00:42:29 +0300 Subject: [PATCH 061/234] fix: missing question marks --- .../nuxt/src/components/runtime/client-delayed-component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index c2751450a5..9ab87f6e6d 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -14,7 +14,7 @@ export const createLazyIOClientPage = (componentLoader: Component) => { 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) + vnode = createStaticVNode(getFragmentHTML(instance.vnode.el ?? null, true)?.join('') || '', 1) } const isIntersecting = ref(false) const el: Ref = ref(null) @@ -47,7 +47,7 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { 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) + vnode = createStaticVNode(getFragmentHTML(instance.vnode.el ?? null, true)?.join('') || '', 1) } const isIdle = ref(false) let idleHandle: number | null = null From e4c99402697374df6356f268529c987c7428bbed Mon Sep 17 00:00:00 2001 From: Julien Huang Date: Sat, 13 Apr 2024 11:06:09 +0200 Subject: [PATCH 062/234] 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 From 5c300a4698a4daba7994d3166442a3b0233aa16f Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 10:38:41 +0000 Subject: [PATCH 063/234] [autofix.ci] apply automated fixes --- .../runtime/client-delayed-component.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index 30760ae440..7cb67d17fc 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -1,4 +1,4 @@ -import { createStaticVNode, defineComponent, getCurrentInstance, h, onBeforeUnmount, onMounted, ref, createVNode } from 'vue' +import { createStaticVNode, createVNode, defineComponent, getCurrentInstance, h, onBeforeUnmount, onMounted, ref } from 'vue' import type { Component, Ref, VNode } from 'vue' // import ClientOnly from '#app/components/client-only' import { useObserver } from '#app/utils' @@ -6,24 +6,22 @@ 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; +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) + h(componentLoader, attrs), ]) } @@ -65,7 +63,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 From 7821e7f441d442df0b8f51652cb49215294b8064 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sat, 13 Apr 2024 19:05:47 +0300 Subject: [PATCH 064/234] feat(schema): add experimental lazy hydration option --- packages/schema/src/config/experimental.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/schema/src/config/experimental.ts b/packages/schema/src/config/experimental.ts index 19403cc38e..6ec7eb1e56 100644 --- a/packages/schema/src/config/experimental.ts +++ b/packages/schema/src/config/experimental.ts @@ -204,6 +204,13 @@ export default defineUntypedSchema({ }, }, + /** + * Experimental built-in delayed component hydration + * + * This enables components to lazily hydrate when needed, improving performance for sites with components below-the-fold + */ + componentLazyHydration: false, + /** * Config schema support * @see [Nuxt Issue #15592](https://github.com/nuxt/nuxt/issues/15592) From 162908aac09b87977f77845d5cd25489107b6d59 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 16:08:11 +0000 Subject: [PATCH 065/234] [autofix.ci] apply automated fixes --- packages/schema/src/config/experimental.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/schema/src/config/experimental.ts b/packages/schema/src/config/experimental.ts index 6ec7eb1e56..081a385186 100644 --- a/packages/schema/src/config/experimental.ts +++ b/packages/schema/src/config/experimental.ts @@ -206,11 +206,11 @@ export default defineUntypedSchema({ /** * Experimental built-in delayed component hydration - * + * * This enables components to lazily hydrate when needed, improving performance for sites with components below-the-fold - */ + */ componentLazyHydration: false, - + /** * Config schema support * @see [Nuxt Issue #15592](https://github.com/nuxt/nuxt/issues/15592) From fd0adea17935fe6b18edd95bb343d2dc4d614836 Mon Sep 17 00:00:00 2001 From: julien huang Date: Sat, 13 Apr 2024 20:14:36 +0200 Subject: [PATCH 066/234] fix: return render function and not vnode directly --- .../nuxt/src/components/runtime/client-delayed-component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index 7cb67d17fc..e5a17a9808 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -18,9 +18,9 @@ function elementIsVisibleInViewport (el: Element) { export const createLazyIOClientPage = (componentLoader: Component) => { return defineComponent({ inheritAttrs: false, - setup (_, { attrs }) { + setup (_, { attrs }) { if (import.meta.server) { - return h('div', {}, [ + return () => h('div', {}, [ h(componentLoader, attrs), ]) } From 13949edeb35abfa1b7f4a2ea6d8fca958b880a9e Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sat, 13 Apr 2024 22:37:03 +0300 Subject: [PATCH 067/234] wip: wait for response with the component --- test/basic.test.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 7836bc369b..eee6462fa6 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2572,9 +2572,10 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', async () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') - expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(0) - await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) - await page.waitForLoadState('networkidle') expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(1) + const response = page.waitForResponse(response => response.url().includes("DelayedWrapperTestComponent") && response.status() === 200) + await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) + const resolvedResponse = await response + await page.waitForLoadState('networkidle') }) }) From 9b8d33babfe154dc44260d13205a0c1d0c937370 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sat, 13 Apr 2024 22:39:23 +0300 Subject: [PATCH 068/234] fix: remove unused var --- test/basic.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index eee6462fa6..24854e23cc 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2575,7 +2575,7 @@ describe('lazy import components', () => { expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(1) const response = page.waitForResponse(response => response.url().includes("DelayedWrapperTestComponent") && response.status() === 200) await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) - const resolvedResponse = await response + await response await page.waitForLoadState('networkidle') }) }) From 8eaf057ffe9e015d90575cbc7769da529e3b8b3e Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 19:41:39 +0000 Subject: [PATCH 069/234] [autofix.ci] apply automated fixes --- test/basic.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 24854e23cc..080d62de30 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2573,7 +2573,7 @@ describe('lazy import components', () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(1) - const response = page.waitForResponse(response => response.url().includes("DelayedWrapperTestComponent") && response.status() === 200) + const response = page.waitForResponse(response => response.url().includes('DelayedWrapperTestComponent') && response.status() === 200) await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) await response await page.waitForLoadState('networkidle') From 0cb15c6bbda6234bb1c53983b2a84ab55d1565e8 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 14 Apr 2024 12:53:49 +0300 Subject: [PATCH 070/234] chore: remove wait for load state --- test/basic.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 080d62de30..88604092b6 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2576,6 +2576,5 @@ describe('lazy import components', () => { const response = page.waitForResponse(response => response.url().includes('DelayedWrapperTestComponent') && response.status() === 200) await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) await response - await page.waitForLoadState('networkidle') }) }) From 850287bf189ff401748884d763ee8e1fadc40640 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 14 Apr 2024 14:04:45 +0300 Subject: [PATCH 071/234] fix: remove the preload for SSR render of delayed hydration --- .../basic/server/plugins/basicRenderDelayedHydration.ts | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/fixtures/basic/server/plugins/basicRenderDelayedHydration.ts diff --git a/test/fixtures/basic/server/plugins/basicRenderDelayedHydration.ts b/test/fixtures/basic/server/plugins/basicRenderDelayedHydration.ts new file mode 100644 index 0000000000..2aa6ac3b63 --- /dev/null +++ b/test/fixtures/basic/server/plugins/basicRenderDelayedHydration.ts @@ -0,0 +1,5 @@ +export default defineNitroPlugin((nitroApp) => { + nitroApp.hooks.hook("render:html", (html, {event})=>{ + html.head = html.head.map((headSection:string)=>headSection.replace(/]+\bhref="\/_nuxt\/DelayedWrapperTestComponent\.([^.]+?)\.js")[^>]+>)/, "")) //.replace(//, "") + }) +}) From b45c3c9cae2e718ddc58b7cef19c0112e0544c49 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 14 Apr 2024 11:06:56 +0000 Subject: [PATCH 072/234] [autofix.ci] apply automated fixes --- .../basic/server/plugins/basicRenderDelayedHydration.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/fixtures/basic/server/plugins/basicRenderDelayedHydration.ts b/test/fixtures/basic/server/plugins/basicRenderDelayedHydration.ts index 2aa6ac3b63..7236caaa0a 100644 --- a/test/fixtures/basic/server/plugins/basicRenderDelayedHydration.ts +++ b/test/fixtures/basic/server/plugins/basicRenderDelayedHydration.ts @@ -1,5 +1,5 @@ export default defineNitroPlugin((nitroApp) => { - nitroApp.hooks.hook("render:html", (html, {event})=>{ - html.head = html.head.map((headSection:string)=>headSection.replace(/]+\bhref="\/_nuxt\/DelayedWrapperTestComponent\.([^.]+?)\.js")[^>]+>)/, "")) //.replace(//, "") - }) + nitroApp.hooks.hook('render:html', (html, { event }) => { + html.head = html.head.map((headSection: string) => headSection.replace(/]+\bhref="\/_nuxt\/DelayedWrapperTestComponent\.([^.]+?)\.js")[^>]+>)/, '')) // .replace(//, "") + }) }) From 3076e02082283cb4c3f7a9f5d5c7d94e927a2715 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 2 Jun 2024 06:47:41 +0000 Subject: [PATCH 073/234] [autofix.ci] apply automated fixes --- .../basic/server/plugins/basicRenderDelayedHydration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/basic/server/plugins/basicRenderDelayedHydration.ts b/test/fixtures/basic/server/plugins/basicRenderDelayedHydration.ts index 7236caaa0a..6e7c740305 100644 --- a/test/fixtures/basic/server/plugins/basicRenderDelayedHydration.ts +++ b/test/fixtures/basic/server/plugins/basicRenderDelayedHydration.ts @@ -1,5 +1,5 @@ export default defineNitroPlugin((nitroApp) => { nitroApp.hooks.hook('render:html', (html, { event }) => { - html.head = html.head.map((headSection: string) => headSection.replace(/]+\bhref="\/_nuxt\/DelayedWrapperTestComponent\.([^.]+?)\.js")[^>]+>)/, '')) // .replace(//, "") + html.head = html.head.map((headSection: string) => headSection.replace(/]+\bhref="\/_nuxt\/DelayedWrapperTestComponent\.([^.]+)\.js")[^>]+>)/, '')) // .replace(//, "") }) }) From fa46fe3e4f2b9e2a538483544819aa1054645675 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 2 Jun 2024 17:12:01 +0300 Subject: [PATCH 074/234] feat: refactor regex, add network idle --- packages/nuxt/src/components/loader.ts | 31 ++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/nuxt/src/components/loader.ts b/packages/nuxt/src/components/loader.ts index 9134151385..b768ee462a 100644 --- a/packages/nuxt/src/components/loader.ts +++ b/packages/nuxt/src/components/loader.ts @@ -43,7 +43,7 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { const s = new MagicString(code) // replace `_resolveComponent("...")` to direct import - s.replace(/(?<=[ (])_?resolveComponent\(\s*["'](lazy-|Lazy)?([^'"]*)["'][^)]*\)/g, (full: string, lazy: string, name: string) => { + s.replace(/(?<=[ (])_?resolveComponent\(\s*["'](lazy-|Lazy)?(Idle|Visible|idle-|visible-)?([^'"]*)["'][^)]*\)/g, (full: string, lazy: string, modifier: string, name: string) => { const component = findComponent(components, name, options.mode) if (component) { // @ts-expect-error TODO: refactor to nuxi @@ -72,6 +72,31 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { } if (lazy) { + if (modifier) { + switch (modifier) { + case "Visible": + case "visible-": + imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) + imports.add(genImport(clientDelayedComponentRuntime, [{ name: 'createLazyIOClientPage' }])) + identifier += '_delayedIO' + imports.add(`const ${identifier} = createLazyIOClientPage(__defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)))`) + break; + case "Idle": + case "idle-": + imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) + imports.add(genImport(clientDelayedComponentRuntime, [{ name: 'createLazyNetworkClientPage' }])) + identifier += '_delayedNetwork' + imports.add(`const ${identifier} = createLazyNetworkClientPage(__defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)))`) + break; + } + + } + else { + imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) + identifier += '_lazy' + imports.add(`const ${identifier} = __defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)${isClientOnly ? '.then(c => createClientOnly(c))' : ''})`) + } + } // Temporary hardcoded check to verify runtime functionality if (name === 'DelayedWrapperTestComponent') { imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) @@ -79,9 +104,7 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { identifier += '_delayedIO' imports.add(`const ${identifier} = createLazyIOClientPage(__defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)))`) } else { - imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) - identifier += '_lazy' - imports.add(`const ${identifier} = __defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)${isClientOnly ? '.then(c => createClientOnly(c))' : ''})`) + } } else { imports.add(genImport(component.filePath, [{ name: component._raw ? 'default' : component.export, as: identifier }])) From b1e66e5c05c70efad19d83ef160c63f387d85b74 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 2 Jun 2024 17:13:03 +0300 Subject: [PATCH 075/234] fix: return component when server-side --- .../nuxt/src/components/runtime/client-delayed-component.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index e5a17a9808..431aae4bb0 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -64,6 +64,11 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { return defineComponent({ inheritAttrs: false, setup (_, { attrs }) { + if (import.meta.server) { + return () => h('div', {}, [ + h(componentLoader, attrs), + ]) + } const nuxt = useNuxtApp() const instance = getCurrentInstance()! let vnode: VNode | null = null From 4616c172e0571b5d9ef63f29f7243255da133494 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 2 Jun 2024 17:18:40 +0300 Subject: [PATCH 076/234] fix: remove leftover previous code --- packages/nuxt/src/components/loader.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/nuxt/src/components/loader.ts b/packages/nuxt/src/components/loader.ts index b768ee462a..92052bb544 100644 --- a/packages/nuxt/src/components/loader.ts +++ b/packages/nuxt/src/components/loader.ts @@ -95,17 +95,7 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) identifier += '_lazy' imports.add(`const ${identifier} = __defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)${isClientOnly ? '.then(c => createClientOnly(c))' : ''})`) - } - } - // Temporary hardcoded check to verify runtime functionality - if (name === 'DelayedWrapperTestComponent') { - imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) - imports.add(genImport(clientDelayedComponentRuntime, [{ name: 'createLazyIOClientPage' }])) - identifier += '_delayedIO' - imports.add(`const ${identifier} = createLazyIOClientPage(__defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)))`) - } else { - - } + } } else { imports.add(genImport(component.filePath, [{ name: component._raw ? 'default' : component.export, as: identifier }])) From 2ac90f37b6264d0e93b1d603ebf3af01e0184659 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 2 Jun 2024 14:20:58 +0000 Subject: [PATCH 077/234] [autofix.ci] apply automated fixes --- packages/nuxt/src/components/loader.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/nuxt/src/components/loader.ts b/packages/nuxt/src/components/loader.ts index 92052bb544..e330fc983a 100644 --- a/packages/nuxt/src/components/loader.ts +++ b/packages/nuxt/src/components/loader.ts @@ -74,28 +74,26 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { if (lazy) { if (modifier) { switch (modifier) { - case "Visible": - case "visible-": + case 'Visible': + case 'visible-': imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) imports.add(genImport(clientDelayedComponentRuntime, [{ name: 'createLazyIOClientPage' }])) identifier += '_delayedIO' imports.add(`const ${identifier} = createLazyIOClientPage(__defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)))`) - break; - case "Idle": - case "idle-": + break + case 'Idle': + case 'idle-': imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) imports.add(genImport(clientDelayedComponentRuntime, [{ name: 'createLazyNetworkClientPage' }])) identifier += '_delayedNetwork' imports.add(`const ${identifier} = createLazyNetworkClientPage(__defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)))`) - break; + break } - - } - else { + } else { imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) identifier += '_lazy' imports.add(`const ${identifier} = __defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)${isClientOnly ? '.then(c => createClientOnly(c))' : ''})`) - } + } } else { imports.add(genImport(component.filePath, [{ name: component._raw ? 'default' : component.export, as: identifier }])) From f5937803f81216cc26cfa5c578e05ca3a6d592a9 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 2 Jun 2024 21:51:05 +0300 Subject: [PATCH 078/234] chore: rename component --- ...elayedWrapperTestComponent.vue => DelayedHydration.client.vue} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/fixtures/basic/components/{DelayedWrapperTestComponent.vue => DelayedHydration.client.vue} (100%) diff --git a/test/fixtures/basic/components/DelayedWrapperTestComponent.vue b/test/fixtures/basic/components/DelayedHydration.client.vue similarity index 100% rename from test/fixtures/basic/components/DelayedWrapperTestComponent.vue rename to test/fixtures/basic/components/DelayedHydration.client.vue From 6f806878fdf44768770cb252a68104ea153343d3 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 2 Jun 2024 21:51:33 +0300 Subject: [PATCH 079/234] Create DelayedHydration.server.vue --- test/fixtures/basic/components/DelayedHydration.server.vue | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/fixtures/basic/components/DelayedHydration.server.vue diff --git a/test/fixtures/basic/components/DelayedHydration.server.vue b/test/fixtures/basic/components/DelayedHydration.server.vue new file mode 100644 index 0000000000..8030f66079 --- /dev/null +++ b/test/fixtures/basic/components/DelayedHydration.server.vue @@ -0,0 +1,5 @@ + From b143038cefbc7623fe31bbac3e335d0a6d0c0f2d Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 2 Jun 2024 21:55:58 +0300 Subject: [PATCH 080/234] fix: change name --- test/fixtures/basic/pages/lazy-import-components/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/basic/pages/lazy-import-components/index.vue b/test/fixtures/basic/pages/lazy-import-components/index.vue index eafa6d8375..440ca5216d 100644 --- a/test/fixtures/basic/pages/lazy-import-components/index.vue +++ b/test/fixtures/basic/pages/lazy-import-components/index.vue @@ -6,6 +6,6 @@
This is a very tall div
- + From b1c498bea70fddf49149c5dce0d5725db3ceeb05 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 2 Jun 2024 21:56:49 +0300 Subject: [PATCH 081/234] test: named LazyVisible components --- test/basic.test.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 0fd2b6787d..3dd305d1c9 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2637,11 +2637,10 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', async () => { expect(html).not.toContain('This shouldn\'t be visible at first!') + expect(html).toContain('This should be visible at first!') const { page } = await renderPage('/lazy-import-components') - expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(1) - const response = page.waitForResponse(response => response.url().includes('DelayedWrapperTestComponent') && response.status() === 200) await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) - await response + expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(1) }) }) From d04d244799d6390a2d6115868d63462cff574877 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 2 Jun 2024 22:04:03 +0300 Subject: [PATCH 082/234] fix: remove line --- test/basic.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 3dd305d1c9..604be88e8a 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2637,7 +2637,6 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', async () => { expect(html).not.toContain('This shouldn\'t be visible at first!') - expect(html).toContain('This should be visible at first!') const { page } = await renderPage('/lazy-import-components') await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(1) From 41c4b53af7c25dd1807e64e3c0feb8d1c0b59ae0 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 2 Jun 2024 22:33:25 +0300 Subject: [PATCH 083/234] chore: use new component --- test/fixtures/basic/pages/lazy-import-components/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/basic/pages/lazy-import-components/index.vue b/test/fixtures/basic/pages/lazy-import-components/index.vue index 440ca5216d..347083a83e 100644 --- a/test/fixtures/basic/pages/lazy-import-components/index.vue +++ b/test/fixtures/basic/pages/lazy-import-components/index.vue @@ -6,6 +6,6 @@
This is a very tall div
- + From 28647ecb300d87435570d8d50f405c0b90b05aff Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 2 Jun 2024 23:24:40 +0300 Subject: [PATCH 084/234] chore: attempt to wait for network idle --- test/basic.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/basic.test.ts b/test/basic.test.ts index 604be88e8a..d9cd367b99 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2639,6 +2639,8 @@ describe('lazy import components', () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) + expect(await page.locator('body').getByText('This should be visible at first!').all()).toHaveLength(1) + await page.waitForLoadState('networkidle') expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(1) }) }) From 66dd659821f0ebb84e97e90dd19ba9a3703b0954 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 2 Jun 2024 23:40:37 +0300 Subject: [PATCH 085/234] fix: delayed hydration for network idle --- .../nuxt/src/components/runtime/client-delayed-component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index 431aae4bb0..851ee38a60 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -72,7 +72,7 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { const nuxt = useNuxtApp() const instance = getCurrentInstance()! let vnode: VNode | null = null - if (import.meta.client && nuxt.isHydrating && instance.vnode?.el) { + if (nuxt.isHydrating && instance.vnode?.el) { vnode = createStaticVNode(getFragmentHTML(instance.vnode.el ?? null, true)?.join('') || '', 1) } const isIdle = ref(false) @@ -90,7 +90,7 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { idleHandle = null } }) - return () => isIdle.value ? h(componentLoader, attrs) : vnode + return () => isIdle.value ? h(componentLoader, attrs) : (instance.vnode.el && nuxt.isHydrating) ? createVNode(createStaticVNode(getFragmentHTML(instance.vnode.el ?? null, true)?.join('') || '', 1)) : null }, }) } From 2a244f056fb22ce2ec8f32f7e3ad7af604ba21aa Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 2 Jun 2024 23:43:29 +0300 Subject: [PATCH 086/234] chore: remove unused vnode --- .../nuxt/src/components/runtime/client-delayed-component.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index 851ee38a60..77a5bdd037 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -71,10 +71,6 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { } const nuxt = useNuxtApp() const instance = getCurrentInstance()! - let vnode: VNode | null = null - if (nuxt.isHydrating && instance.vnode?.el) { - vnode = createStaticVNode(getFragmentHTML(instance.vnode.el ?? null, true)?.join('') || '', 1) - } const isIdle = ref(false) let idleHandle: number | null = null onMounted(() => { From 5051c861b191563e58998f8f96fdf81287a3df96 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 2 Jun 2024 23:49:48 +0300 Subject: [PATCH 087/234] chore: remove VNode type --- .../nuxt/src/components/runtime/client-delayed-component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index 77a5bdd037..67f1071d3f 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -1,5 +1,5 @@ import { createStaticVNode, createVNode, defineComponent, getCurrentInstance, h, onBeforeUnmount, onMounted, ref } from 'vue' -import type { Component, Ref, VNode } from 'vue' +import type { Component, Ref } from 'vue' // import ClientOnly from '#app/components/client-only' import { useObserver } from '#app/utils' import { getFragmentHTML } from '#app/components/utils' From 6d3ec1714bca4b64f3adf33f70414ca34c1ac244 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 2 Jun 2024 23:59:43 +0300 Subject: [PATCH 088/234] Update and rename DelayedHydration.client.vue to DelayedVisible.client.vue --- test/fixtures/basic/components/DelayedHydration.client.vue | 5 ----- test/fixtures/basic/components/DelayedVisible.client.vue | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 test/fixtures/basic/components/DelayedHydration.client.vue create mode 100644 test/fixtures/basic/components/DelayedVisible.client.vue diff --git a/test/fixtures/basic/components/DelayedHydration.client.vue b/test/fixtures/basic/components/DelayedHydration.client.vue deleted file mode 100644 index d42e68faf4..0000000000 --- a/test/fixtures/basic/components/DelayedHydration.client.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/test/fixtures/basic/components/DelayedVisible.client.vue b/test/fixtures/basic/components/DelayedVisible.client.vue new file mode 100644 index 0000000000..138d4fa96f --- /dev/null +++ b/test/fixtures/basic/components/DelayedVisible.client.vue @@ -0,0 +1,5 @@ + From 90fb0d590412ae47d274f94941e271886747ea30 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 3 Jun 2024 00:00:08 +0300 Subject: [PATCH 089/234] Update and rename DelayedHydration.server.vue to DelayedVisible.server.vue --- test/fixtures/basic/components/DelayedHydration.server.vue | 5 ----- test/fixtures/basic/components/DelayedVisible.server.vue | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 test/fixtures/basic/components/DelayedHydration.server.vue create mode 100644 test/fixtures/basic/components/DelayedVisible.server.vue diff --git a/test/fixtures/basic/components/DelayedHydration.server.vue b/test/fixtures/basic/components/DelayedHydration.server.vue deleted file mode 100644 index 8030f66079..0000000000 --- a/test/fixtures/basic/components/DelayedHydration.server.vue +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/test/fixtures/basic/components/DelayedVisible.server.vue b/test/fixtures/basic/components/DelayedVisible.server.vue new file mode 100644 index 0000000000..49f2e0262a --- /dev/null +++ b/test/fixtures/basic/components/DelayedVisible.server.vue @@ -0,0 +1,5 @@ + From a26603fafb18c24d991f69c17efb9d27c8860a68 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 3 Jun 2024 00:00:54 +0300 Subject: [PATCH 090/234] Create DelayedNetwork.client.vue --- test/fixtures/basic/components/DelayedNetwork.client.vue | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/fixtures/basic/components/DelayedNetwork.client.vue diff --git a/test/fixtures/basic/components/DelayedNetwork.client.vue b/test/fixtures/basic/components/DelayedNetwork.client.vue new file mode 100644 index 0000000000..c76f76b8d6 --- /dev/null +++ b/test/fixtures/basic/components/DelayedNetwork.client.vue @@ -0,0 +1,5 @@ + From d32cfdf09baac6884bfa5cf5fda2f51d4ef874aa Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 3 Jun 2024 00:01:15 +0300 Subject: [PATCH 091/234] Create DelayedNetwork.server.vue --- test/fixtures/basic/components/DelayedNetwork.server.vue | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/fixtures/basic/components/DelayedNetwork.server.vue diff --git a/test/fixtures/basic/components/DelayedNetwork.server.vue b/test/fixtures/basic/components/DelayedNetwork.server.vue new file mode 100644 index 0000000000..65192055ea --- /dev/null +++ b/test/fixtures/basic/components/DelayedNetwork.server.vue @@ -0,0 +1,5 @@ + From f4f8ca08dcf9ee6ead5824656d702daba53c6397 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 3 Jun 2024 00:02:31 +0300 Subject: [PATCH 092/234] chore: add network idle component --- test/fixtures/basic/pages/lazy-import-components/index.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/fixtures/basic/pages/lazy-import-components/index.vue b/test/fixtures/basic/pages/lazy-import-components/index.vue index 347083a83e..797f018700 100644 --- a/test/fixtures/basic/pages/lazy-import-components/index.vue +++ b/test/fixtures/basic/pages/lazy-import-components/index.vue @@ -3,9 +3,10 @@ +
This is a very tall div
- + From 9746137b28230a1801cb21614a3317406d20e8e6 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 3 Jun 2024 00:04:43 +0300 Subject: [PATCH 093/234] test: add network idle test --- test/basic.test.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index d9cd367b99..61f0fb19c2 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2638,10 +2638,15 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', async () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') - await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) - expect(await page.locator('body').getByText('This should be visible at first!').all()).toHaveLength(1) + expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(1) + expect(await page.locator('body').getByText('This shouldn\'t be visible at first with network!').all()).toHaveLength(0) await page.waitForLoadState('networkidle') - expect(await page.locator('body').getByText('This shouldn\'t be visible at first!').all()).toHaveLength(1) + expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(0) + expect(await page.locator('body').getByText('This shouldn\'t be visible at first with network!').all()).toHaveLength(1) + await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) + expect(await page.locator('body').getByText('This should be visible at first with viewport!').all()).toHaveLength(1) + await page.waitForLoadState('networkidle') + expect(await page.locator('body').getByText('This shouldn\'t be visible at first with viewport!').all()).toHaveLength(1) }) }) From 502383feea9149d72e5d19d7a0ed60dba5a5d8d7 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 3 Jun 2024 00:38:58 +0300 Subject: [PATCH 095/234] chore: attempt to remove the pre-idle checks --- test/basic.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 61f0fb19c2..ba058f0ab0 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2638,8 +2638,8 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', async () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') - expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(1) - expect(await page.locator('body').getByText('This shouldn\'t be visible at first with network!').all()).toHaveLength(0) + //expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(1) + //expect(await page.locator('body').getByText('This shouldn\'t be visible at first with network!').all()).toHaveLength(0) await page.waitForLoadState('networkidle') expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(0) expect(await page.locator('body').getByText('This shouldn\'t be visible at first with network!').all()).toHaveLength(1) From a0f43b45f21ae20bdbae13f5410c3941bb267aee Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 2 Jun 2024 21:41:22 +0000 Subject: [PATCH 096/234] [autofix.ci] apply automated fixes --- test/basic.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index ba058f0ab0..50d53e17fb 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2638,8 +2638,8 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', async () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') - //expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(1) - //expect(await page.locator('body').getByText('This shouldn\'t be visible at first with network!').all()).toHaveLength(0) + // expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(1) + // expect(await page.locator('body').getByText('This shouldn\'t be visible at first with network!').all()).toHaveLength(0) await page.waitForLoadState('networkidle') expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(0) expect(await page.locator('body').getByText('This shouldn\'t be visible at first with network!').all()).toHaveLength(1) From 440f4cc91a16807533d88698f22ad45a418c7682 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 3 Jun 2024 00:55:28 +0300 Subject: [PATCH 097/234] chore: temporarily remove network component --- test/basic.test.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 50d53e17fb..d57bbba774 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2638,11 +2638,6 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', async () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') - // expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(1) - // expect(await page.locator('body').getByText('This shouldn\'t be visible at first with network!').all()).toHaveLength(0) - await page.waitForLoadState('networkidle') - expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(0) - expect(await page.locator('body').getByText('This shouldn\'t be visible at first with network!').all()).toHaveLength(1) await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) expect(await page.locator('body').getByText('This should be visible at first with viewport!').all()).toHaveLength(1) await page.waitForLoadState('networkidle') From a53c5897a179534988465738f296b16e3cac21b5 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 3 Jun 2024 09:35:49 +0300 Subject: [PATCH 099/234] docs: add docs for delayed hydration --- .../2.directory-structure/1.components.md | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/2.guide/2.directory-structure/1.components.md b/docs/2.guide/2.directory-structure/1.components.md index 0cdf5cb4b6..1fb9a35026 100644 --- a/docs/2.guide/2.directory-structure/1.components.md +++ b/docs/2.guide/2.directory-structure/1.components.md @@ -119,6 +119,37 @@ const show = ref(false) ``` +## Delayed Hydration + +In real world applications, some pages may include a lot of content and a lot of components, and most of the time not all of them need to be interactive right off the bat. Having them all load eagerly can negatively impact performance and bundle size. + +In order to optimize the page, you may want to delay the hydration of some components until they're visible, or until the browser is done with more important tasks for example. Nuxt has first class support for delayed hydration components and can help you reduce your boilerplate along the way. + +Nuxt has reserved component prefixes that will handle this delayed hydration for you, that extend dynamic Imports. By prefixing your component with `LazyVisible`, Nuxt will automatically handle your component and delay its hydration until it will be on screen. + +```vue [pages/index.vue] + +``` + +If you need the component to load as soon as possible, but not block the critical rendering path, you can use the `LazyIdle` prefix, which would handle your component's hydration whenever the browser goes idle. + +```vue [pages/index.vue] + +``` + +::important +Since Nuxt uses `LazyIdle` and `LazyVisible` to handle delayed hydration, you should avoid naming your components that, as dynamic imports will break for you. Delayed Hydration would still be possible by adding the prefix, for example to a component named `IdleBar`: +`` +:: + ## Direct Imports You can also explicitly import components from `#components` if you want or need to bypass Nuxt's auto-importing functionality. From e0a2f219db21ce9df64be07d89baf0f816551060 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 06:36:24 +0000 Subject: [PATCH 100/234] [autofix.ci] apply automated fixes --- docs/2.guide/2.directory-structure/1.components.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/2.guide/2.directory-structure/1.components.md b/docs/2.guide/2.directory-structure/1.components.md index 1fb9a35026..36f951ee69 100644 --- a/docs/2.guide/2.directory-structure/1.components.md +++ b/docs/2.guide/2.directory-structure/1.components.md @@ -119,9 +119,9 @@ const show = ref(false) ``` -## Delayed Hydration +## Delayed Hydration -In real world applications, some pages may include a lot of content and a lot of components, and most of the time not all of them need to be interactive right off the bat. Having them all load eagerly can negatively impact performance and bundle size. +In real world applications, some pages may include a lot of content and a lot of components, and most of the time not all of them need to be interactive right off the bat. Having them all load eagerly can negatively impact performance and bundle size. In order to optimize the page, you may want to delay the hydration of some components until they're visible, or until the browser is done with more important tasks for example. Nuxt has first class support for delayed hydration components and can help you reduce your boilerplate along the way. From 2f2cb8e0cf1a171282b16c5a5112edeed814f0ef Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 3 Jun 2024 10:22:52 +0300 Subject: [PATCH 101/234] fix: only enable delayed hydration with the experimental flag --- packages/nuxt/src/components/loader.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nuxt/src/components/loader.ts b/packages/nuxt/src/components/loader.ts index e330fc983a..8b218cacf0 100644 --- a/packages/nuxt/src/components/loader.ts +++ b/packages/nuxt/src/components/loader.ts @@ -41,13 +41,13 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { const imports = new Set() const map = new Map() const s = new MagicString(code) - + const nuxt = tryUseNuxt() // replace `_resolveComponent("...")` to direct import s.replace(/(?<=[ (])_?resolveComponent\(\s*["'](lazy-|Lazy)?(Idle|Visible|idle-|visible-)?([^'"]*)["'][^)]*\)/g, (full: string, lazy: string, modifier: string, name: string) => { const component = findComponent(components, name, options.mode) if (component) { // @ts-expect-error TODO: refactor to nuxi - if (component._internal_install && tryUseNuxt()?.options.test === false) { + if (component._internal_install && nuxt?.options.test === false) { // @ts-expect-error TODO: refactor to nuxi import('../core/features').then(({ installNuxtModule }) => installNuxtModule(component._internal_install)) } @@ -72,7 +72,7 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { } if (lazy) { - if (modifier) { + if (nuxt?.options.experimental.componentLazyHydration === true && modifier) { switch (modifier) { case 'Visible': case 'visible-': From b764ae93be200a428acd63b7936a08cdfa85a0fc Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 3 Jun 2024 10:39:14 +0300 Subject: [PATCH 102/234] fix: add componentLazyHydration --- test/fixtures/basic/nuxt.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/fixtures/basic/nuxt.config.ts b/test/fixtures/basic/nuxt.config.ts index d312d55944..a96ea889b3 100644 --- a/test/fixtures/basic/nuxt.config.ts +++ b/test/fixtures/basic/nuxt.config.ts @@ -238,6 +238,7 @@ export default defineNuxtConfig({ renderJsonPayloads: process.env.TEST_PAYLOAD !== 'js', headNext: true, inlineRouteRules: true, + componentLazyHydration: true }, appConfig: { fromNuxtConfig: true, From 4eb39d10626be50c58acf1e96d5dd058d48815e5 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 07:41:35 +0000 Subject: [PATCH 103/234] [autofix.ci] apply automated fixes --- test/fixtures/basic/nuxt.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/basic/nuxt.config.ts b/test/fixtures/basic/nuxt.config.ts index a96ea889b3..beda5b9fcb 100644 --- a/test/fixtures/basic/nuxt.config.ts +++ b/test/fixtures/basic/nuxt.config.ts @@ -238,7 +238,7 @@ export default defineNuxtConfig({ renderJsonPayloads: process.env.TEST_PAYLOAD !== 'js', headNext: true, inlineRouteRules: true, - componentLazyHydration: true + componentLazyHydration: true, }, appConfig: { fromNuxtConfig: true, From b87a70c825837a205f13df3bfee2c8af996f7881 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 9 Jun 2024 21:07:39 +0300 Subject: [PATCH 104/234] chore: attempt a higher div --- test/fixtures/basic/pages/lazy-import-components/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/basic/pages/lazy-import-components/index.vue b/test/fixtures/basic/pages/lazy-import-components/index.vue index 797f018700..ef64df44e6 100644 --- a/test/fixtures/basic/pages/lazy-import-components/index.vue +++ b/test/fixtures/basic/pages/lazy-import-components/index.vue @@ -4,7 +4,7 @@ -
+
This is a very tall div
From ee7d8fa20e691b67ec8ecb6fd973a7064635d288 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 9 Jun 2024 22:25:06 +0300 Subject: [PATCH 105/234] fix: evaluate scroll after initial check --- test/basic.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index ef6a80a3cc..78f54165ae 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2639,8 +2639,8 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', async () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') - await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) expect(await page.locator('body').getByText('This should be visible at first with viewport!').all()).toHaveLength(1) + await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) await page.waitForLoadState('networkidle') expect(await page.locator('body').getByText('This shouldn\'t be visible at first with viewport!').all()).toHaveLength(1) }) From b9e735424152a7e2d0b70cd5068e1f59f13b6995 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 9 Jun 2024 23:05:34 +0300 Subject: [PATCH 106/234] chore: fix network comp display --- .../nuxt/src/components/runtime/client-delayed-component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index 67f1071d3f..a5c1a918c8 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -86,7 +86,9 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { idleHandle = null } }) - return () => isIdle.value ? h(componentLoader, attrs) : (instance.vnode.el && nuxt.isHydrating) ? createVNode(createStaticVNode(getFragmentHTML(instance.vnode.el ?? null, true)?.join('') || '', 1)) : null + return () => h('div', {}, [ + isIdle.value ? h(componentLoader, attrs) : (instance.vnode.el && nuxt.isHydrating) ? createVNode(createStaticVNode(getFragmentHTML(instance.vnode.el ?? null, true)?.join('') || '', 1)) : null + ]) }, }) } From 79a805fa8cad5f31929a3507e5e178deba44057d Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 9 Jun 2024 20:08:02 +0000 Subject: [PATCH 107/234] [autofix.ci] apply automated fixes --- .../nuxt/src/components/runtime/client-delayed-component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/nuxt/src/components/runtime/client-delayed-component.ts b/packages/nuxt/src/components/runtime/client-delayed-component.ts index a5c1a918c8..016ba8de1a 100644 --- a/packages/nuxt/src/components/runtime/client-delayed-component.ts +++ b/packages/nuxt/src/components/runtime/client-delayed-component.ts @@ -86,8 +86,8 @@ export const createLazyNetworkClientPage = (componentLoader: Component) => { idleHandle = null } }) - return () => h('div', {}, [ - isIdle.value ? h(componentLoader, attrs) : (instance.vnode.el && nuxt.isHydrating) ? createVNode(createStaticVNode(getFragmentHTML(instance.vnode.el ?? null, true)?.join('') || '', 1)) : null + return () => h('div', {}, [ + isIdle.value ? h(componentLoader, attrs) : (instance.vnode.el && nuxt.isHydrating) ? createVNode(createStaticVNode(getFragmentHTML(instance.vnode.el ?? null, true)?.join('') || '', 1)) : null, ]) }, }) From 53d73baa483cafb4adb8c159f5fb7d6cfa32386b Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 9 Jun 2024 23:34:04 +0300 Subject: [PATCH 108/234] test: attempt network idle test --- test/basic.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/basic.test.ts b/test/basic.test.ts index 78f54165ae..01d5df3aed 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2639,7 +2639,10 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', async () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') + expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(1) expect(await page.locator('body').getByText('This should be visible at first with viewport!').all()).toHaveLength(1) + await page.waitForLoadState('networkidle') + expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(0) await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) await page.waitForLoadState('networkidle') expect(await page.locator('body').getByText('This shouldn\'t be visible at first with viewport!').all()).toHaveLength(1) From c8cbcfc56e7b9eb06efe6db2b56f3eb53d0803c9 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 9 Jun 2024 23:36:04 +0300 Subject: [PATCH 109/234] chore: use Promise.all to try and be faster than initial idle time --- test/basic.test.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 01d5df3aed..431ad4fe73 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2639,8 +2639,12 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', async () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') - expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(1) - expect(await page.locator('body').getByText('This should be visible at first with viewport!').all()).toHaveLength(1) + const [allNetwork, allViewport] = await Promise.all([ + page.locator('body').getByText('This should be visible at first with network!').all(), + page.locator('body').getByText('This should be visible at first with viewport!').all(), + ]) + expect(allNetwork).toHaveLength(1) + expect(allViewport).toHaveLength(1) await page.waitForLoadState('networkidle') expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(0) await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) From b0f4f64d271f3c6648786cbb0680e7915bafa055 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 9 Jun 2024 23:43:41 +0300 Subject: [PATCH 110/234] chore: retry without promise.all --- test/basic.test.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 431ad4fe73..01d5df3aed 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2639,12 +2639,8 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', async () => { expect(html).not.toContain('This shouldn\'t be visible at first!') const { page } = await renderPage('/lazy-import-components') - const [allNetwork, allViewport] = await Promise.all([ - page.locator('body').getByText('This should be visible at first with network!').all(), - page.locator('body').getByText('This should be visible at first with viewport!').all(), - ]) - expect(allNetwork).toHaveLength(1) - expect(allViewport).toHaveLength(1) + expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(1) + expect(await page.locator('body').getByText('This should be visible at first with viewport!').all()).toHaveLength(1) await page.waitForLoadState('networkidle') expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(0) await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) From ed16dc4a7df9acf22d912d8691b8bbf92d40fa53 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Sun, 9 Jun 2024 23:45:22 +0300 Subject: [PATCH 111/234] chore: extract import --- packages/nuxt/src/components/loader.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/nuxt/src/components/loader.ts b/packages/nuxt/src/components/loader.ts index 8b218cacf0..b65385f3e9 100644 --- a/packages/nuxt/src/components/loader.ts +++ b/packages/nuxt/src/components/loader.ts @@ -72,25 +72,23 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => { } if (lazy) { + imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) if (nuxt?.options.experimental.componentLazyHydration === true && modifier) { switch (modifier) { case 'Visible': case 'visible-': - imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) imports.add(genImport(clientDelayedComponentRuntime, [{ name: 'createLazyIOClientPage' }])) identifier += '_delayedIO' imports.add(`const ${identifier} = createLazyIOClientPage(__defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)))`) break case 'Idle': case 'idle-': - imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) imports.add(genImport(clientDelayedComponentRuntime, [{ name: 'createLazyNetworkClientPage' }])) identifier += '_delayedNetwork' imports.add(`const ${identifier} = createLazyNetworkClientPage(__defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)))`) break } } else { - imports.add(genImport('vue', [{ name: 'defineAsyncComponent', as: '__defineAsyncComponent' }])) identifier += '_lazy' imports.add(`const ${identifier} = __defineAsyncComponent(${genDynamicImport(component.filePath, { interopDefault: false })}.then(c => c.${component.export ?? 'default'} || c)${isClientOnly ? '.then(c => createClientOnly(c))' : ''})`) } From 53c8bb92fef116718379d896828c8ba2f99769bb Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 10 Jun 2024 00:20:49 +0300 Subject: [PATCH 112/234] chore: attempt to check through html --- test/basic.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 01d5df3aed..1beb9f6ad2 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2637,7 +2637,7 @@ describe('lazy import components', () => { }) it('lazy load delayed hydration comps at the right time', async () => { - expect(html).not.toContain('This shouldn\'t be visible at first!') + expect(html).toContain('This should be visible at first with network!') const { page } = await renderPage('/lazy-import-components') expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(1) expect(await page.locator('body').getByText('This should be visible at first with viewport!').all()).toHaveLength(1) From 2b765e0b8838e275197d31739ec19f5eea214ec0 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 10 Jun 2024 00:30:13 +0300 Subject: [PATCH 113/234] chore: refactor the test --- test/basic.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index 1beb9f6ad2..32e66164f2 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2639,12 +2639,13 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', async () => { expect(html).toContain('This should be visible at first with network!') const { page } = await renderPage('/lazy-import-components') - expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(1) expect(await page.locator('body').getByText('This should be visible at first with viewport!').all()).toHaveLength(1) await page.waitForLoadState('networkidle') expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(0) + expect(await page.locator('body').getByText('This shouldn\'t be visible at first with network!').all()).toHaveLength(1) await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) await page.waitForLoadState('networkidle') + expect(await page.locator('body').getByText('This should be visible at first with viewport!').all()).toHaveLength(0) expect(await page.locator('body').getByText('This shouldn\'t be visible at first with viewport!').all()).toHaveLength(1) }) }) From 1039735d458e73cb79e8a8d6e516b473c8746dbb Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 10 Jun 2024 00:41:55 +0300 Subject: [PATCH 114/234] chore: await another networkidle --- test/basic.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/basic.test.ts b/test/basic.test.ts index 32e66164f2..e15b62df42 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2646,6 +2646,7 @@ describe('lazy import components', () => { await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) await page.waitForLoadState('networkidle') expect(await page.locator('body').getByText('This should be visible at first with viewport!').all()).toHaveLength(0) + await page.waitForLoadState('networkidle') expect(await page.locator('body').getByText('This shouldn\'t be visible at first with viewport!').all()).toHaveLength(1) }) }) From 2089f745a81944a38bf52874e00946b1be0c8c99 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 10 Jun 2024 10:29:32 +0300 Subject: [PATCH 116/234] test: refactor test --- test/basic.test.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index e15b62df42..ea49fff6e9 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2641,12 +2641,9 @@ describe('lazy import components', () => { const { page } = await renderPage('/lazy-import-components') expect(await page.locator('body').getByText('This should be visible at first with viewport!').all()).toHaveLength(1) await page.waitForLoadState('networkidle') - expect(await page.locator('body').getByText('This should be visible at first with network!').all()).toHaveLength(0) expect(await page.locator('body').getByText('This shouldn\'t be visible at first with network!').all()).toHaveLength(1) await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) await page.waitForLoadState('networkidle') - expect(await page.locator('body').getByText('This should be visible at first with viewport!').all()).toHaveLength(0) - await page.waitForLoadState('networkidle') expect(await page.locator('body').getByText('This shouldn\'t be visible at first with viewport!').all()).toHaveLength(1) }) }) From 09f341c3ef605b1fb4b206305604e7f552d87b68 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 10 Jun 2024 10:41:49 +0300 Subject: [PATCH 117/234] chore: revert test page --- test/fixtures/basic/pages/lazy-import-components/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/basic/pages/lazy-import-components/index.vue b/test/fixtures/basic/pages/lazy-import-components/index.vue index ef64df44e6..797f018700 100644 --- a/test/fixtures/basic/pages/lazy-import-components/index.vue +++ b/test/fixtures/basic/pages/lazy-import-components/index.vue @@ -4,7 +4,7 @@ -
+
This is a very tall div
From fb3397701e0217a0ee90d31ecafb03c36c543427 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 10 Jun 2024 11:24:16 +0300 Subject: [PATCH 118/234] chore: revert test --- test/basic.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/basic.test.ts b/test/basic.test.ts index ea49fff6e9..a5432c760d 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2640,8 +2640,6 @@ describe('lazy import components', () => { expect(html).toContain('This should be visible at first with network!') const { page } = await renderPage('/lazy-import-components') expect(await page.locator('body').getByText('This should be visible at first with viewport!').all()).toHaveLength(1) - await page.waitForLoadState('networkidle') - expect(await page.locator('body').getByText('This shouldn\'t be visible at first with network!').all()).toHaveLength(1) await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) await page.waitForLoadState('networkidle') expect(await page.locator('body').getByText('This shouldn\'t be visible at first with viewport!').all()).toHaveLength(1) From 0792f95b86c53cce60bc5ac95f3b503ba10357cb Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 10 Jun 2024 11:41:33 +0300 Subject: [PATCH 119/234] chore: retry network evaluation --- test/basic.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/basic.test.ts b/test/basic.test.ts index a5432c760d..14b76bc235 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -2639,6 +2639,8 @@ describe('lazy import components', () => { it('lazy load delayed hydration comps at the right time', async () => { expect(html).toContain('This should be visible at first with network!') const { page } = await renderPage('/lazy-import-components') + await page.waitForLoadState('networkidle') + expect(await page.locator('body').getByText('This shouldn\'t be visible at first with network!').all()).toHaveLength(1) expect(await page.locator('body').getByText('This should be visible at first with viewport!').all()).toHaveLength(1) await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)) await page.waitForLoadState('networkidle') From 6be28279f1121c5d9a14eaab03768e96bae18664 Mon Sep 17 00:00:00 2001 From: Michael Brevard Date: Mon, 10 Jun 2024 11:56:41 +0300 Subject: [PATCH 120/234] docs: fix case --- docs/2.guide/2.directory-structure/1.components.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/2.guide/2.directory-structure/1.components.md b/docs/2.guide/2.directory-structure/1.components.md index 36f951ee69..331b254774 100644 --- a/docs/2.guide/2.directory-structure/1.components.md +++ b/docs/2.guide/2.directory-structure/1.components.md @@ -121,11 +121,11 @@ const show = ref(false) ## Delayed Hydration -In real world applications, some pages may include a lot of content and a lot of components, and most of the time not all of them need to be interactive right off the bat. Having them all load eagerly can negatively impact performance and bundle size. +In real world applications, some pages may include a lot of content and a lot of components, and most of the time not all of them need to be interactive as soon as the page is loaded. Having them all load eagerly can negatively impact performance and increase bundle size. In order to optimize the page, you may want to delay the hydration of some components until they're visible, or until the browser is done with more important tasks for example. Nuxt has first class support for delayed hydration components and can help you reduce your boilerplate along the way. -Nuxt has reserved component prefixes that will handle this delayed hydration for you, that extend dynamic Imports. By prefixing your component with `LazyVisible`, Nuxt will automatically handle your component and delay its hydration until it will be on screen. +Nuxt has reserved component prefixes that will handle this delayed hydration for you, that extend dynamic imports. By prefixing your component with `LazyVisible`, Nuxt will automatically handle your component and delay its hydration until it will be on screen. ```vue [pages/index.vue] ``` +### Custom hydration triggers + +If you would like to override the default hydration triggers when dealing with delayed hydration, like changing the timeout or the options for the intersection observer, you can do so by supplying a `loader` prop to your lazy components. + +```vue [pages/index.vue] + +``` + +::read-more{to="/docs/api/utils/create-idle-loader"} +:: + +::read-more{to="/docs/api/utils/create-visible-loader"} +:: + ::important Since Nuxt uses `LazyIdle` and `LazyVisible` to handle delayed hydration, you should avoid naming your components that, as dynamic imports will break for you. Delayed hydration would still be possible by adding the prefix, for example to a component named `IdleBar`: `` diff --git a/docs/3.api/3.utils/create-idle-loader.md b/docs/3.api/3.utils/create-idle-loader.md new file mode 100644 index 0000000000..8adbebe56f --- /dev/null +++ b/docs/3.api/3.utils/create-idle-loader.md @@ -0,0 +1,33 @@ +--- +title: 'createIdleLoader' +description: A utility function to customize delayed hydration based on network idle time. +links: + - label: Source + icon: i-simple-icons-github + to: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/hydrate.ts + size: xs +--- + +You can use this utility to customize the timeout of delayed hydration components based on network idle time. + +## Parameters + +- `options`: `{ timeout }` + +## Example + +If you would like to give a timeout of 5 seconds for the components: + +```vue [pages/index.vue] +