mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 13:45:18 +00:00
feat(nuxt): add useRuntimeHook
composable (#29741)
This commit is contained in:
parent
09885b87ef
commit
59c28d5a5a
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 { useId } from './id'
|
||||
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 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 { defineNuxtLink } from './components/index'
|
||||
|
@ -47,7 +47,7 @@ export interface RuntimeNuxtHooks {
|
||||
'app:chunkError': (options: { error: any }) => HookResult
|
||||
'app:data:refresh': (keys?: string[]) => 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
|
||||
'page:start': (Component?: VNode) => HookResult
|
||||
'page:finish': (Component?: VNode) => HookResult
|
||||
|
@ -109,6 +109,10 @@ const granularAppPresets: InlinePreset[] = [
|
||||
imports: ['useRouteAnnouncer'],
|
||||
from: '#app/composables/route-announcer',
|
||||
},
|
||||
{
|
||||
imports: ['useRuntimeHook'],
|
||||
from: '#app/composables/runtime-hook',
|
||||
},
|
||||
]
|
||||
|
||||
export const scriptsStubsPreset = {
|
||||
|
@ -20,6 +20,7 @@ import { callOnce } from '#app/composables/once'
|
||||
import { useLoadingIndicator } from '#app/composables/loading-indicator'
|
||||
import { useRouteAnnouncer } from '#app/composables/route-announcer'
|
||||
import { encodeURL, resolveRouteObject } from '#app/composables/router'
|
||||
import { useRuntimeHook } from '#app/composables/runtime-hook'
|
||||
|
||||
import { asyncDataDefaults, nuxtDefaultErrorValue } from '#build/nuxt.config.mjs'
|
||||
|
||||
@ -95,6 +96,7 @@ describe('composables', () => {
|
||||
'abortNavigation',
|
||||
'setPageLayout',
|
||||
'defineNuxtComponent',
|
||||
'useRuntimeHook',
|
||||
]
|
||||
const skippedComposables: string[] = [
|
||||
'addRouteMiddleware',
|
||||
@ -580,6 +582,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`', () => {
|
||||
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 })`.]')
|
||||
|
Loading…
Reference in New Issue
Block a user