mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-28 00:22:05 +00:00
feat(nuxt): add useRuntimeHook
composable (#29741)
This commit is contained in:
parent
edef832700
commit
2aa4daab92
43
docs/3.api/2.composables/use-runtime-hook.md
Normal file
43
docs/3.api/2.composables/use-runtime-hook.md
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
title: useRuntimeHook
|
||||||
|
description: Registers a runtime hook in a Nuxt application and ensures it is properly disposed of when the scope is destroyed.
|
||||||
|
links:
|
||||||
|
- label: Source
|
||||||
|
icon: i-simple-icons-github
|
||||||
|
to: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/runtime-hook.ts
|
||||||
|
size: xs
|
||||||
|
---
|
||||||
|
|
||||||
|
::important
|
||||||
|
This composable is available in Nuxt v3.14+.
|
||||||
|
::
|
||||||
|
|
||||||
|
```ts [signature]
|
||||||
|
function useRuntimeHook<THookName extends keyof RuntimeNuxtHooks>(
|
||||||
|
name: THookName,
|
||||||
|
fn: RuntimeNuxtHooks[THookName] extends HookCallback ? RuntimeNuxtHooks[THookName] : never
|
||||||
|
): void
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
- `name`: The name of the runtime hook to register. You can see the full list of [runtime Nuxt hooks here](/docs/api/advanced/hooks#app-hooks-runtime).
|
||||||
|
- `fn`: The callback function to execute when the hook is triggered. The function signature varies based on the hook name.
|
||||||
|
|
||||||
|
### Returns
|
||||||
|
|
||||||
|
The composable doesn't return a value, but it automatically unregisters the hook when the component's scope is destroyed.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```vue twoslash [pages/index.vue]
|
||||||
|
<script setup lang="ts">
|
||||||
|
// Register a hook that runs every time a link is prefetched, but which will be
|
||||||
|
// automatically cleaned up (and not called again) when the component is unmounted
|
||||||
|
useRuntimeHook('link:prefetch', (link) => {
|
||||||
|
console.log('Prefetching', link)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
```
|
@ -38,3 +38,4 @@ export { useRequestURL } from './url'
|
|||||||
export { usePreviewMode } from './preview'
|
export { usePreviewMode } from './preview'
|
||||||
export { useId } from './id'
|
export { useId } from './id'
|
||||||
export { useRouteAnnouncer } from './route-announcer'
|
export { useRouteAnnouncer } from './route-announcer'
|
||||||
|
export { useRuntimeHook } from './runtime-hook'
|
||||||
|
21
packages/nuxt/src/app/composables/runtime-hook.ts
Normal file
21
packages/nuxt/src/app/composables/runtime-hook.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { onScopeDispose } from 'vue'
|
||||||
|
import type { HookCallback } from 'hookable'
|
||||||
|
import { useNuxtApp } from '../nuxt'
|
||||||
|
import type { RuntimeNuxtHooks } from '../nuxt'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a runtime hook in a Nuxt application and ensures it is properly disposed of when the scope is destroyed.
|
||||||
|
* @param name - The name of the hook to register.
|
||||||
|
* @param fn - The callback function to be executed when the hook is triggered.
|
||||||
|
* @since 3.14.0
|
||||||
|
*/
|
||||||
|
export function useRuntimeHook<THookName extends keyof RuntimeNuxtHooks> (
|
||||||
|
name: THookName,
|
||||||
|
fn: RuntimeNuxtHooks[THookName] extends HookCallback ? RuntimeNuxtHooks[THookName] : never,
|
||||||
|
): void {
|
||||||
|
const nuxtApp = useNuxtApp()
|
||||||
|
|
||||||
|
const unregister = nuxtApp.hook(name, fn)
|
||||||
|
|
||||||
|
onScopeDispose(unregister)
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
export { applyPlugin, applyPlugins, callWithNuxt, createNuxtApp, defineAppConfig, defineNuxtPlugin, definePayloadPlugin, isNuxtPlugin, registerPluginHooks, tryUseNuxtApp, useNuxtApp, useRuntimeConfig } from './nuxt'
|
export { applyPlugin, applyPlugins, callWithNuxt, createNuxtApp, defineAppConfig, defineNuxtPlugin, definePayloadPlugin, isNuxtPlugin, registerPluginHooks, tryUseNuxtApp, useNuxtApp, useRuntimeConfig } from './nuxt'
|
||||||
export type { CreateOptions, NuxtApp, NuxtPayload, NuxtPluginIndicator, NuxtSSRContext, ObjectPlugin, Plugin, PluginEnvContext, PluginMeta, ResolvedPluginMeta, RuntimeNuxtHooks } from './nuxt'
|
export type { CreateOptions, NuxtApp, NuxtPayload, NuxtPluginIndicator, NuxtSSRContext, ObjectPlugin, Plugin, PluginEnvContext, PluginMeta, ResolvedPluginMeta, RuntimeNuxtHooks } from './nuxt'
|
||||||
|
|
||||||
export { defineNuxtComponent, useAsyncData, useLazyAsyncData, useNuxtData, refreshNuxtData, clearNuxtData, useHydration, callOnce, useState, clearNuxtState, clearError, createError, isNuxtError, showError, useError, useFetch, useLazyFetch, useCookie, refreshCookie, onPrehydrate, prerenderRoutes, useRequestHeaders, useRequestEvent, useRequestFetch, setResponseStatus, useResponseHeader, onNuxtReady, abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter, preloadComponents, prefetchComponents, preloadRouteComponents, isPrerendered, loadPayload, preloadPayload, definePayloadReducer, definePayloadReviver, getAppManifest, getRouteRules, reloadNuxtApp, useRequestURL, usePreviewMode, useId, useRouteAnnouncer, useHead, useSeoMeta, useServerSeoMeta } from './composables/index'
|
export { defineNuxtComponent, useAsyncData, useLazyAsyncData, useNuxtData, refreshNuxtData, clearNuxtData, useHydration, callOnce, useState, clearNuxtState, clearError, createError, isNuxtError, showError, useError, useFetch, useLazyFetch, useCookie, refreshCookie, onPrehydrate, prerenderRoutes, useRequestHeaders, useRequestEvent, useRequestFetch, setResponseStatus, useResponseHeader, onNuxtReady, abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useRouter, preloadComponents, prefetchComponents, preloadRouteComponents, isPrerendered, loadPayload, preloadPayload, definePayloadReducer, definePayloadReviver, getAppManifest, getRouteRules, reloadNuxtApp, useRequestURL, usePreviewMode, useId, useRouteAnnouncer, useHead, useSeoMeta, useServerSeoMeta, useRuntimeHook } from './composables/index'
|
||||||
export type { AddRouteMiddlewareOptions, AsyncData, AsyncDataOptions, AsyncDataRequestStatus, CookieOptions, CookieRef, FetchResult, NuxtAppManifest, NuxtAppManifestMeta, NuxtError, ReloadNuxtAppOptions, RouteMiddleware, UseFetchOptions } from './composables/index'
|
export type { AddRouteMiddlewareOptions, AsyncData, AsyncDataOptions, AsyncDataRequestStatus, CookieOptions, CookieRef, FetchResult, NuxtAppManifest, NuxtAppManifestMeta, NuxtError, ReloadNuxtAppOptions, RouteMiddleware, UseFetchOptions } from './composables/index'
|
||||||
|
|
||||||
export { defineNuxtLink } from './components/index'
|
export { defineNuxtLink } from './components/index'
|
||||||
|
@ -45,7 +45,7 @@ export interface RuntimeNuxtHooks {
|
|||||||
'app:chunkError': (options: { error: any }) => HookResult
|
'app:chunkError': (options: { error: any }) => HookResult
|
||||||
'app:data:refresh': (keys?: string[]) => HookResult
|
'app:data:refresh': (keys?: string[]) => HookResult
|
||||||
'app:manifest:update': (meta?: NuxtAppManifestMeta) => HookResult
|
'app:manifest:update': (meta?: NuxtAppManifestMeta) => HookResult
|
||||||
'dev:ssr-logs': (logs: LogObject[]) => void | Promise<void>
|
'dev:ssr-logs': (logs: LogObject[]) => HookResult
|
||||||
'link:prefetch': (link: string) => HookResult
|
'link:prefetch': (link: string) => HookResult
|
||||||
'page:start': (Component?: VNode) => HookResult
|
'page:start': (Component?: VNode) => HookResult
|
||||||
'page:finish': (Component?: VNode) => HookResult
|
'page:finish': (Component?: VNode) => HookResult
|
||||||
|
@ -109,6 +109,10 @@ const granularAppPresets: InlinePreset[] = [
|
|||||||
imports: ['useRouteAnnouncer'],
|
imports: ['useRouteAnnouncer'],
|
||||||
from: '#app/composables/route-announcer',
|
from: '#app/composables/route-announcer',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
imports: ['useRuntimeHook'],
|
||||||
|
from: '#app/composables/runtime-hook',
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export const scriptsStubsPreset = {
|
export const scriptsStubsPreset = {
|
||||||
|
@ -20,6 +20,7 @@ import { callOnce } from '#app/composables/once'
|
|||||||
import { useLoadingIndicator } from '#app/composables/loading-indicator'
|
import { useLoadingIndicator } from '#app/composables/loading-indicator'
|
||||||
import { useRouteAnnouncer } from '#app/composables/route-announcer'
|
import { useRouteAnnouncer } from '#app/composables/route-announcer'
|
||||||
import { encodeURL, resolveRouteObject } from '#app/composables/router'
|
import { encodeURL, resolveRouteObject } from '#app/composables/router'
|
||||||
|
import { useRuntimeHook } from '#app/composables/runtime-hook'
|
||||||
|
|
||||||
registerEndpoint('/api/test', defineEventHandler(event => ({
|
registerEndpoint('/api/test', defineEventHandler(event => ({
|
||||||
method: event.method,
|
method: event.method,
|
||||||
@ -93,6 +94,7 @@ describe('composables', () => {
|
|||||||
'abortNavigation',
|
'abortNavigation',
|
||||||
'setPageLayout',
|
'setPageLayout',
|
||||||
'defineNuxtComponent',
|
'defineNuxtComponent',
|
||||||
|
'useRuntimeHook',
|
||||||
]
|
]
|
||||||
const skippedComposables: string[] = [
|
const skippedComposables: string[] = [
|
||||||
'addRouteMiddleware',
|
'addRouteMiddleware',
|
||||||
@ -577,6 +579,36 @@ describe.skipIf(process.env.TEST_MANIFEST === 'manifest-off')('app manifests', (
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('useRuntimeHook', () => {
|
||||||
|
it('types work', () => {
|
||||||
|
// @ts-expect-error should not allow unknown hooks
|
||||||
|
useRuntimeHook('test', () => {})
|
||||||
|
useRuntimeHook('app:beforeMount', (_app) => {
|
||||||
|
// @ts-expect-error argument should be typed
|
||||||
|
_app = 'test'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should call hooks', async () => {
|
||||||
|
const nuxtApp = useNuxtApp()
|
||||||
|
let called = 1
|
||||||
|
const wrapper = await mountSuspended(defineNuxtComponent({
|
||||||
|
setup () {
|
||||||
|
useRuntimeHook('test-hook' as any, () => {
|
||||||
|
called++
|
||||||
|
})
|
||||||
|
},
|
||||||
|
render: () => h('div', 'hi there'),
|
||||||
|
}))
|
||||||
|
expect(called).toBe(1)
|
||||||
|
await nuxtApp.callHook('test-hook' as any)
|
||||||
|
expect(called).toBe(2)
|
||||||
|
wrapper.unmount()
|
||||||
|
await nuxtApp.callHook('test-hook' as any)
|
||||||
|
expect(called).toBe(2)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('routing utilities: `navigateTo`', () => {
|
describe('routing utilities: `navigateTo`', () => {
|
||||||
it('navigateTo should disallow navigation to external URLs by default', () => {
|
it('navigateTo should disallow navigation to external URLs by default', () => {
|
||||||
expect(() => navigateTo('https://test.com')).toThrowErrorMatchingInlineSnapshot('[Error: Navigating to an external URL is not allowed by default. Use `navigateTo(url, { external: true })`.]')
|
expect(() => navigateTo('https://test.com')).toThrowErrorMatchingInlineSnapshot('[Error: Navigating to an external URL is not allowed by default. Use `navigateTo(url, { external: true })`.]')
|
||||||
|
Loading…
Reference in New Issue
Block a user