feat(nuxt): tryUseNuxtApp composable (#25031)

This commit is contained in:
Danila Rodichkin 2024-01-18 12:59:59 +03:00 committed by GitHub
parent 2bfe01d26f
commit 6b651cf7bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 48 additions and 3 deletions

View File

@ -28,6 +28,8 @@ You can think of it as **Runtime Core**.
This context can be accessed using [`useNuxtApp()`](/docs/api/composables/use-nuxt-app) composable within Nuxt plugins and `<script setup>` and vue composables.
Global usage is possible for the browser but not on the server, to avoid sharing context between users.
Since [`useNuxtApp`](/docs/api/composables/use-nuxt-app) throws an exception if context is currently unavailable, if your composable does not always require `nuxtApp`, you can use [`tryUseNuxtApp`](/docs/api/composables/use-nuxt-app#tryusenuxtapp) instead, which will return `null` instead of throwing an exception.
To extend the `nuxtApp` interface and hook into different stages or access contexts, we can use [Nuxt Plugins](/docs/guide/directory-structure/plugins).
Check [Nuxt App](/docs/api/composables/use-nuxt-app) for more information about this interface.

View File

@ -30,6 +30,8 @@ export function useMyComposable () {
}
```
If your composable does not always need `nuxtApp` or you simply want to check if it is present or not, since [`useNuxtApp`](/docs/api/composables/use-nuxt-app) throws an exception, you can use [`tryUseNuxtApp`](/docs/api/composables/use-nuxt-app#tryusenuxtapp) instead.
Plugins also receive `nuxtApp` as the first argument for convenience.
:read-more{to="/docs/guide/directory-structure/plugins"}

View File

@ -16,6 +16,8 @@ const nuxtApp = useNuxtApp()
</script>
```
If runtime context is unavailable in your scope, `useNuxtApp` will throw an exception when called. You can use [`tryUseNuxtApp`](#tryusenuxtapp) instead for composables that do not require `nuxtApp`, or to simply check if context is available or not without an exception.
## Methods
### `provide (name, value)`
@ -257,3 +259,22 @@ Native async context support works currently in Bun and Node.
::
:read-more{to="/docs/guide/going-further/experimental-features#asynccontext"}
## tryUseNuxtApp
This function works exactly the same as `useNuxtApp`, but returns `null` if context is unavailable instead of throwing an exception.
You can use it for composables that do not require `nuxtApp`, or to simply check if context is available or not without an exception.
Example usage:
```ts [composable.ts]
export function useStandType() {
// Always works on the client
if (tryUseNuxtApp()) {
return useRuntimeConfig().public.STAND_TYPE
} else {
return process.env.STAND_TYPE
}
}
```

View File

@ -439,8 +439,10 @@ export function callWithNuxt<T extends (...args: any[]) => any> (nuxt: NuxtApp |
/*@__NO_SIDE_EFFECTS__*/
/**
* Returns the current Nuxt instance.
*
* Returns `null` if Nuxt instance is unavailable.
*/
export function useNuxtApp (): NuxtApp {
export function tryUseNuxtApp (): NuxtApp | null {
let nuxtAppInstance
if (hasInjectionContext()) {
nuxtAppInstance = getCurrentInstance()?.appContext.app.$nuxt
@ -448,6 +450,18 @@ export function useNuxtApp (): NuxtApp {
nuxtAppInstance = nuxtAppInstance || nuxtAppCtx.tryUse()
return nuxtAppInstance || null
}
/*@__NO_SIDE_EFFECTS__*/
/**
* Returns the current Nuxt instance.
*
* Throws an error if Nuxt instance is unavailable.
*/
export function useNuxtApp (): NuxtApp {
const nuxtAppInstance = tryUseNuxtApp()
if (!nuxtAppInstance) {
if (import.meta.dev) {
throw new Error('[nuxt] A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function. This is probably not a Nuxt bug. Find out more at `https://nuxt.com/docs/guide/concepts/auto-imports#vue-and-nuxt-composables`.')

View File

@ -18,7 +18,7 @@ const granularAppPresets: InlinePreset[] = [
imports: ['defineNuxtLink']
},
{
imports: ['useNuxtApp', 'defineNuxtPlugin', 'definePayloadPlugin', 'useRuntimeConfig', 'defineAppConfig'],
imports: ['useNuxtApp', 'tryUseNuxtApp', 'defineNuxtPlugin', 'definePayloadPlugin', 'useRuntimeConfig', 'defineAppConfig'],
from: '#app/nuxt'
},
{

View File

@ -2130,6 +2130,12 @@ describe.skipIf(process.env.TEST_CONTEXT !== 'async')('Async context', () => {
})
})
describe.skipIf(process.env.TEST_CONTEXT === 'async')('Async context', () => {
it('should be unavailable', async () => {
expect(await $fetch('/async-context')).toContain('&quot;hasApp&quot;: false')
})
})
describe.skipIf(isWindows)('useAsyncData', () => {
it('works after useNuxtData call', async () => {
const page = await createPage('/useAsyncData/nuxt-data')

View File

@ -12,7 +12,7 @@ async function fn1 () {
async function fn2 () {
await delay()
const app = useNuxtApp()
const app = tryUseNuxtApp()
return {
hasApp: !!app
}