diff --git a/docs/content/3.api/1.composables/prefetch-components.md b/docs/content/3.api/1.composables/prefetch-components.md new file mode 100644 index 0000000000..21fcce0b3b --- /dev/null +++ b/docs/content/3.api/1.composables/prefetch-components.md @@ -0,0 +1,24 @@ +# `prefetchComponents` + +::StabilityEdge +:: + +Nuxt provides composables and utilities to give you fine-grained control over prefetching and preloading components. + +> Prefetching component downloads the code in the background, this is based on the assumption that the component will likely be used for rendering, enabling the component to load instantly if and when the user requests it. The component is downloaded and cached for anticipated future use without the user making an explicit request for it. + +You can use `prefetchComponents` to manually prefetch individual components that have been registered globally in your Nuxt app. (By default Nuxt registers these as async components.) You must use the Pascal-cased version of the component name. + +```js +await prefetchComponents('MyGlobalComponent') + +await prefetchComponents(['MyGlobalComponent1', 'MyGlobalComponent2']) +``` + +::alert{icon=👉} +Current implementation behaves exactly the same as [`preloadComponents`](/api/composables/preload-components) by preloading components instead of just prefetching we are working to improve this behavior. +:: + +::alert{icon=👉} +Currently, on server, `prefetchComponents` will have no effect. +:: diff --git a/docs/content/3.api/1.composables/preload-components.md b/docs/content/3.api/1.composables/preload-components.md new file mode 100644 index 0000000000..a051d051fa --- /dev/null +++ b/docs/content/3.api/1.composables/preload-components.md @@ -0,0 +1,20 @@ +# `preloadComponents` + +::StabilityEdge +:: + +Nuxt provides composables and utilities to give you fine-grained control over prefetching and preloading components. + +> Preloading components loads components that your page will need very soon, which you want to start loading early in rendering lifecycle. This ensures they are available earlier and are less likely to block the page's render, improving performance. + +You can use `preloadComponents` to manually preload individual components that have been registered globally in your Nuxt app. (By default Nuxt registers these as async components.) You must use the Pascal-cased version of the component name. + +```js +await preloadComponents('MyGlobalComponent') + +await preloadComponents(['MyGlobalComponent1', 'MyGlobalComponent2']) +``` + +::alert{icon=👉} +Currently, on server, `preloadComponents` will have no effect. +:: diff --git a/packages/nuxt/src/app/composables/index.ts b/packages/nuxt/src/app/composables/index.ts index 92bcf1b0d1..b3a31a9f43 100644 --- a/packages/nuxt/src/app/composables/index.ts +++ b/packages/nuxt/src/app/composables/index.ts @@ -12,3 +12,4 @@ export type { CookieOptions, CookieRef } from './cookie' export { useRequestHeaders, useRequestEvent, setResponseStatus } from './ssr' export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, navigateTo, useRoute, useActiveRoute, useRouter } from './router' export type { AddRouteMiddlewareOptions, RouteMiddleware } from './router' +export { preloadComponents, prefetchComponents } from './preload' diff --git a/packages/nuxt/src/app/composables/preload.ts b/packages/nuxt/src/app/composables/preload.ts new file mode 100644 index 0000000000..cb888cbce6 --- /dev/null +++ b/packages/nuxt/src/app/composables/preload.ts @@ -0,0 +1,33 @@ +import type { Component } from 'vue' +import { useNuxtApp } from '#app' + +/** + * Preload a component or components that have been globally registered. + * + * @param components Pascal-cased name or names of components to prefetch + */ +export const preloadComponents = async (components: string | string[]) => { + if (process.server) { return } + const nuxtApp = useNuxtApp() + + components = Array.isArray(components) ? components : [components] + await Promise.all(components.map(name => _loadAsyncComponent(nuxtApp.vueApp._context.components[name]))) +} + +/** + * Prefetch a component or components that have been globally registered. + * + * @param components Pascal-cased name or names of components to prefetch + */ +export const prefetchComponents = (components: string | string[]) => { + // TODO + return preloadComponents(components) +} + +// --- Internal --- + +function _loadAsyncComponent (component: Component) { + if ((component as any)?.__asyncLoader && !(component as any).__asyncResolved) { + return (component as any).__asyncLoader() + } +} diff --git a/packages/nuxt/src/components/templates.ts b/packages/nuxt/src/components/templates.ts index 62879252e8..d74119e106 100644 --- a/packages/nuxt/src/components/templates.ts +++ b/packages/nuxt/src/components/templates.ts @@ -11,7 +11,7 @@ export interface ComponentsTemplateContext { } export type ImportMagicCommentsOptions = { - chunkName:string + chunkName: string prefetch?: boolean | number preload?: boolean | number } diff --git a/packages/nuxt/src/imports/presets.ts b/packages/nuxt/src/imports/presets.ts index e9ca159bfd..5655717733 100644 --- a/packages/nuxt/src/imports/presets.ts +++ b/packages/nuxt/src/imports/presets.ts @@ -51,7 +51,9 @@ const appPreset = defineUnimportPreset({ 'createError', 'defineNuxtLink', 'useAppConfig', - 'defineAppConfig' + 'defineAppConfig', + 'preloadComponents', + 'prefetchComponents' ] }) diff --git a/test/basic.test.ts b/test/basic.test.ts index cee7e7c7e8..e69626feb0 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -360,6 +360,12 @@ describe('automatically keyed composables', () => { }) }) +describe('prefetching', () => { + it('should prefetch components', async () => { + await expectNoClientErrors('/prefetch/components') + }) +}) + if (process.env.NUXT_TEST_DEV) { describe('detecting invalid root nodes', () => { it('should detect invalid root nodes in pages', async () => { diff --git a/test/fixtures/basic/pages/prefetch/components.vue b/test/fixtures/basic/pages/prefetch/components.vue new file mode 100644 index 0000000000..6b9fe525f0 --- /dev/null +++ b/test/fixtures/basic/pages/prefetch/components.vue @@ -0,0 +1,9 @@ + + +