feat(nuxt): add onNuxtReady composable (#9478)

This commit is contained in:
Daniel Roe 2022-12-05 10:09:58 +00:00 committed by GitHub
parent d36d115524
commit 4c4249dc33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 52 additions and 24 deletions

View File

@ -0,0 +1,19 @@
---
title: "onNuxtReady"
description: The onNuxtReady composable allows running a callback after your app has finished initializing.
---
# `onNuxtReady`
The `onNuxtReady` composable allows running a callback after your app has finished initializing. It is ideal for running code that should not block the initial rendering of your app.
```ts
export default defineNuxtPlugin(() => {
onNuxtReady(async () => {
const myAnalyticsLibrary = await import('my-big-analytics-library')
// do something with myAnalyticsLibrary
})
})
```
It is 'safe' to run even after your app has initialized. In this case, then the code will be registered to run in the next idle callback.

View File

@ -0,0 +1,16 @@
// Polyfills for Safari support
// https://caniuse.com/requestidlecallback
export const requestIdleCallback: Window['requestIdleCallback'] = process.server
? undefined as any
: (globalThis.requestIdleCallback || ((cb) => {
const start = Date.now()
const idleDeadline = {
didTimeout: false,
timeRemaining: () => Math.max(0, 50 - (Date.now() - start))
}
return setTimeout(() => { cb(idleDeadline) }, 1)
}))
export const cancelIdleCallback: Window['cancelIdleCallback'] = process.server
? null as any
: (globalThis.cancelIdleCallback || ((id) => { clearTimeout(id) }))

View File

@ -3,8 +3,10 @@ import type { RouteLocationRaw } from 'vue-router'
import { hasProtocol } from 'ufo' import { hasProtocol } from 'ufo'
import { preloadRouteComponents } from '../composables/preload' import { preloadRouteComponents } from '../composables/preload'
import { onNuxtReady } from '../composables/ready'
import { navigateTo, useRouter } from '../composables/router' import { navigateTo, useRouter } from '../composables/router'
import { useNuxtApp } from '../nuxt' import { useNuxtApp } from '../nuxt'
import { cancelIdleCallback, requestIdleCallback } from '../compat/idle-callback'
const firstNonUndefined = <T> (...args: (T | undefined)[]) => args.find(arg => arg !== undefined) const firstNonUndefined = <T> (...args: (T | undefined)[]) => args.find(arg => arg !== undefined)
@ -42,23 +44,6 @@ export type NuxtLinkProps = {
ariaCurrentValue?: string ariaCurrentValue?: string
} }
// Polyfills for Safari support
// https://caniuse.com/requestidlecallback
const requestIdleCallback: Window['requestIdleCallback'] = process.server
? undefined as any
: (globalThis.requestIdleCallback || ((cb) => {
const start = Date.now()
const idleDeadline = {
didTimeout: false,
timeRemaining: () => Math.max(0, 50 - (Date.now() - start))
}
return setTimeout(() => { cb(idleDeadline) }, 1)
}))
const cancelIdleCallback: Window['cancelIdleCallback'] = process.server
? null as any
: (globalThis.cancelIdleCallback || ((id) => { clearTimeout(id) }))
export function defineNuxtLink (options: NuxtLinkOptions) { export function defineNuxtLink (options: NuxtLinkOptions) {
const componentName = options.componentName || 'NuxtLink' const componentName = options.componentName || 'NuxtLink'
@ -197,7 +182,7 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
let unobserve: Function | null = null let unobserve: Function | null = null
onMounted(() => { onMounted(() => {
const observer = useObserver() const observer = useObserver()
function registerCallback () { onNuxtReady(() => {
idleId = requestIdleCallback(() => { idleId = requestIdleCallback(() => {
if (el?.value?.tagName) { if (el?.value?.tagName) {
unobserve = observer!.observe(el.value, async () => { unobserve = observer!.observe(el.value, async () => {
@ -211,12 +196,7 @@ export function defineNuxtLink (options: NuxtLinkOptions) {
}) })
} }
}) })
} })
if (nuxtApp.isHydrating) {
nuxtApp.hooks.hookOnce('app:suspense:resolve', registerCallback)
} else {
registerCallback()
}
}) })
onBeforeUnmount(() => { onBeforeUnmount(() => {
if (idleId) { cancelIdleCallback(idleId) } if (idleId) { cancelIdleCallback(idleId) }

View File

@ -10,6 +10,7 @@ export type { FetchResult, UseFetchOptions } from './fetch'
export { useCookie } from './cookie' export { useCookie } from './cookie'
export type { CookieOptions, CookieRef } from './cookie' export type { CookieOptions, CookieRef } from './cookie'
export { useRequestHeaders, useRequestEvent, setResponseStatus } from './ssr' export { useRequestHeaders, useRequestEvent, setResponseStatus } from './ssr'
export { onNuxtReady } from './ready'
export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter } from './router' export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter } from './router'
export type { AddRouteMiddlewareOptions, RouteMiddleware } from './router' export type { AddRouteMiddlewareOptions, RouteMiddleware } from './router'
export { preloadComponents, prefetchComponents, preloadRouteComponents } from './preload' export { preloadComponents, prefetchComponents, preloadRouteComponents } from './preload'

View File

@ -0,0 +1,11 @@
import { useNuxtApp } from '../nuxt'
import { requestIdleCallback } from '../compat/idle-callback'
export const onNuxtReady = (callback: () => any) => {
const nuxtApp = useNuxtApp()
if (nuxtApp.isHydrating) {
nuxtApp.hooks.hookOnce('app:suspense:resolve', () => { requestIdleCallback(callback) })
} else {
requestIdleCallback(callback)
}
}

View File

@ -37,6 +37,7 @@ const appPreset = defineUnimportPreset({
'useRequestEvent', 'useRequestEvent',
'setResponseStatus', 'setResponseStatus',
'setPageLayout', 'setPageLayout',
'onNuxtReady',
'useRouter', 'useRouter',
'useRoute', 'useRoute',
'defineNuxtRouteMiddleware', 'defineNuxtRouteMiddleware',