useNuxtApp

访问 Nuxt 应用的共享运行时上下文。

useNuxtApp 是一个内置可组合组件,它提供了一种访问 Nuxt 共享运行时上下文(也称为 Nuxt 上下文)的方法,该上下文在客户端和服务器端均可用(但不在 Nitro 路由内)。它帮助你访问 Vue 应用实例、运行时钩子、运行时配置变量和内部状态,例如 ssrContextpayload

¥useNuxtApp is a built-in composable that provides a way to access shared runtime context of Nuxt, also known as the Nuxt context, which is available on both client and server side (but not within Nitro routes). It helps you access the Vue app instance, runtime hooks, runtime config variables and internal states, such as ssrContext and payload.

app.vue
<script setup lang="ts">
const nuxtApp = useNuxtApp()
</script>

如果你的作用域中没有可用的运行时上下文,调用 useNuxtApp 时将抛出异常。你可以将 tryUseNuxtApp 用于不需要 nuxtApp 的可组合项,或者简单地检查上下文是否可用(无异常)。

¥If runtime context is unavailable in your scope, useNuxtApp will throw an exception when called. You can use 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)

nuxtApp 是一个运行时上下文,你可以使用 Nuxt 插件 进行扩展。使用 provide 函数创建 Nuxt 插件,使值和辅助方法在你的 Nuxt 应用中可用于所有可组合项和组件。

¥nuxtApp is a runtime context that you can extend using Nuxt plugins. Use the provide function to create Nuxt plugins to make values and helper methods available in your Nuxt application across all composables and components.

provide 函数接受 namevalue 参数。

¥provide function accepts name and value parameters.

const nuxtApp = useNuxtApp()
nuxtApp.provide('hello', (name) => `Hello ${name}!`)

// Prints "Hello name!"
console.log(nuxtApp.$hello('name'))

如上例所示,$hello 已成为 nuxtApp 上下文中新的自定义部分,并且在所有可访问 nuxtApp 的地方都可用。

¥As you can see in the example above, $hello has become the new and custom part of nuxtApp context and it is available in all places where nuxtApp is accessible.

hook(name, cb)

nuxtApp 中提供的钩子允许你自定义 Nuxt 应用的运行时方面。你可以使用 Vue.js 可组合项和 Nuxt 插件 中的运行时钩子来连接到渲染生命周期。

¥Hooks available in nuxtApp allows you to customize the runtime aspects of your Nuxt application. You can use runtime hooks in Vue.js composables and Nuxt plugins to hook into the rendering lifecycle.

hook 函数有助于通过在特定点挂载到渲染生命周期来添加自定义逻辑。hook 函数主要用于创建 Nuxt 插件。

¥hook function is useful for adding custom logic by hooking into the rendering lifecycle at a specific point. hook function is mostly used when creating Nuxt plugins.

有关 Nuxt 调用的可用运行时钩子,请参阅 运行时钩子

¥See Runtime Hooks for available runtime hooks called by Nuxt.

plugins/test.ts
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hook('page:start', () => {
    /* your code goes here */
  })
  nuxtApp.hook('vue:error', (..._args) => {
    console.log('vue:error')
    // if (import.meta.client) {
    //   console.log(..._args)
    // }
  })
})

callHook(name, ...args)

当使用任何现有钩子调用 callHook 时,都会返回一个 Promise。

¥callHook returns a promise when called with any of the existing hooks.

await nuxtApp.callHook('my-plugin:init')

属性

¥Properties

useNuxtApp() 公开以下属性,你可以使用这些属性来扩展和自定义你的应用,并共享状态、数据和变量。

¥useNuxtApp() exposes the following properties that you can use to extend and customize your app and share state, data and variables.

vueApp

vueApp 是全局的 Vue.js 应用实例,你可以通过 nuxtApp 访问它。

¥vueApp is the global Vue.js application instance that you can access through nuxtApp.

一些实用方法:

¥Some useful methods:

  • component() - 如果同时传递名称字符串和组件定义,则注册全局组件;如果仅传递名称,则检索已注册的组件。
  • directive() - 如果同时传递名称字符串和指令定义,则注册全局自定义指令;如果仅传递名称,则检索已注册的指令 (例子)
  • use() - 安装 Vue.js 插件 (例子)
Read more in https://vuejs.org/api/application.html#application-api.

ssrContext

ssrContext 是在服务器端渲染期间生成的,并且仅在服务器端可用。

¥ssrContext is generated during server-side rendering and it is only available on the server side.

Nuxt 通过 ssrContext 公开以下属性:

¥Nuxt exposes the following properties through ssrContext:

  • url(字符串) - 当前请求 URL。
  • event(unjs/h3 请求事件) - 访问当前路由的请求和响应。
  • payload(对象) - NuxtApp 负载对象。

payload

payload 将数据和状态变量从服务器端暴露给客户端。以下键从服务器端传递后,将在客户端可用:

¥payload exposes data and state variables from server side to client side. The following keys will be available on the client after they have been passed from the server side:

  • serverRendered(布尔值) - 指示响应是否在服务器端渲染。
  • data(对象) - 当你使用 useFetchuseAsyncData 从 API 端点获取数据时,可以从 payload.data 访问生成的有效负载。这些数据会被缓存,有助于避免在多次发出相同请求时获取相同的数据。
    <script setup lang="ts">
    const { data } = await useAsyncData('count', () => $fetch('/api/count'))
    </script>
    

    在上面的示例中,使用 useAsyncData 获取 count 的值后,如果你访问 payload.data,你将看到 { count: 1 } 记录在那里。
    当从 ssrcontext 访问同一个 payload.data 时,你也可以在服务器端访问相同的值。
  • state(对象) - 当你在 Nuxt 中使用 useState 可组合项设置共享状态时,此状态数据是通过 payload.state.[name-of-your-state] 访问的。
    plugins/my-plugin.ts
    export const useColor = () => useState<string>('color', () => 'pink')
    
    export default defineNuxtPlugin((nuxtApp) => {
      if (import.meta.server) {
        const color = useColor()
      }
    })
    

    还可以使用更高级的类型,例如 refreactiveshallowRefshallowReactiveNuxtError
    Nuxt v3.4 起,你可以为 Nuxt 不支持的类型定义自己的 reducer/reviver。

在下面的示例中,我们使用有效负载插件为 Luxon DateTime 类定义了一个 reducer(或序列化器)和一个 reviver(或反序列化器)。

¥In the example below, we define a reducer (or a serializer) and a reviver (or deserializer) for the Luxon DateTime class, using a payload plugin.

plugins/date-time-payload.ts
/**

 * This kind of plugin runs very early in the Nuxt lifecycle, before we revive the payload.

 * You will not have access to the router or other Nuxt-injected properties.

 *  * Note that the "DateTime" string is the type identifier and must

 * be the same on both the reducer and the reviver.
 */
export default definePayloadPlugin((nuxtApp) => {
  definePayloadReducer('DateTime', (value) => {
    return value instanceof DateTime && value.toJSON()
  })
  definePayloadReviver('DateTime', (value) => {
    return DateTime.fromISO(value)
  })
})

isHydrating

使用 nuxtApp.isHydrating(布尔值)检查 Nuxt 应用是否在客户端进行 hydration。

¥Use nuxtApp.isHydrating (boolean) to check if the Nuxt app is hydrating on the client side.

components/nuxt-error-boundary.ts
export default defineComponent({
  setup (_props, { slots, emit }) {
    const nuxtApp = useNuxtApp()
    onErrorCaptured((err) => {
      if (import.meta.client && !nuxtApp.isHydrating) {
        // ...
      }
    })
  }
})

runWithContext

你来这里很可能是因为你收到了 "Nuxt instance unavailable" 消息。请谨慎使用此方法,并报告导致问题的示例,以便最终在框架级别解决问题。

runWithContext 方法旨在用于调用函数并赋予其显式的 Nuxt 上下文。通常情况下,Nuxt 上下文是隐式传递的,你无需担心这一点。但是,在中间件/插件中处理复杂的 async/await 场景时,你可能会遇到异步调用后当前实例被取消设置的情况。

¥The runWithContext method is meant to be used to call a function and give it an explicit Nuxt context. Typically, the Nuxt context is passed around implicitly and you do not need to worry about this. However, when working with complex async/await scenarios in middleware/plugins, you can run into instances where the current instance has been unset after an async call.

middleware/auth.ts
export default defineNuxtRouteMiddleware(async (to, from) => {
  const nuxtApp = useNuxtApp()
  let user
  try {
    user = await fetchUser()
    // the Vue/Nuxt compiler loses context here because of the try/catch block.
  } catch (e) {
    user = null
  }
  if (!user) {
    // apply the correct Nuxt context to our `navigateTo` call.
    return nuxtApp.runWithContext(() => navigateTo('/auth'))
  }
})

用法

¥Usage

const result = nuxtApp.runWithContext(() => functionWithContext())
  • functionWithContext:任何需要当前 Nuxt 应用上下文的函数。此上下文将自动正确应用。

runWithContext 将返回 functionWithContext 返回的内容。

¥runWithContext will return whatever is returned by functionWithContext.

上下文的深度解释

¥A Deeper Explanation of Context

Vue.js Composition API(以及类似 Nuxt 可组合项)依赖于隐式上下文工作。在生命周期中,Vue 会将当前组件的临时实例(以及 Nuxt App 的 Nuxt 临时实例)设置为全局变量,并在同一 tick 中将其取消设置。在服务器端渲染时,会有来自不同用户的多个请求,nuxtApp 运行在同一个全局上下文中。因此,Nuxt 和 Vue 会立即取消设置此全局实例,以避免在两个用户或组件之间泄漏共享引用。

¥Vue.js Composition API (and Nuxt composables similarly) work by depending on an implicit context. During the lifecycle, Vue sets the temporary instance of the current component (and Nuxt temporary instance of nuxtApp) to a global variable and unsets it in same tick. When rendering on the server side, there are multiple requests from different users and nuxtApp running in a same global context. Because of this, Nuxt and Vue immediately unset this global instance to avoid leaking a shared reference between two users or components.

它的含义是什么?Composition API 和 Nuxt Composables 仅在生命周期内以及任何异步操作之前的同一 tick 中可用:

¥What it does mean? The Composition API and Nuxt Composables are only available during lifecycle and in same tick before any async operation:

// --- Vue internal ---
const _vueInstance = null
const getCurrentInstance = () => _vueInstance
// ---

// Vue / Nuxt sets a global variable referencing to current component in _vueInstance when calling setup()
async function setup() {
  getCurrentInstance() // Works
  await someAsyncOperation() // Vue unsets the context in same tick before async operation!
  getCurrentInstance() // null
}

对此的经典解决方案是在第一次调用时将当前实例缓存到局部变量(例如 const instance = getCurrentInstance()),并在下一次可组合调用中使用它,但问题是任何嵌套的可组合调用现在都需要显式接受该实例作为参数,而不是依赖于 Composition-API 的隐式上下文。这是可组合组件的设计限制,本身并非问题。

¥The classic solution to this, is caching the current instance on first call to a local variable like const instance = getCurrentInstance() and use it in the next composable call but the issue is that any nested composable calls now needs to explicitly accept the instance as an argument and not depend on the implicit context of composition-api. This is design limitation with composables and not an issue per-se.

为了克服这一限制,Vue 在编译我们的应用代码时会进行一些后台工作,并在每次调用 <script setup> 后恢复上下文:

¥To overcome this limitation, Vue does some behind the scenes work when compiling our application code and restores context after each call for <script setup>:

const __instance = getCurrentInstance() // Generated by Vue compiler
getCurrentInstance() // Works!
await someAsyncOperation() // Vue unsets the context
__restoreInstance(__instance) // Generated by Vue compiler
getCurrentInstance() // Still works!

有关 Vue 实际功能的更详细描述,请参阅 unjs/unctx#2 (评论)

¥For a better description of what Vue actually does, see unjs/unctx#2 (comment).

解决方案

¥Solution

在这里,可以使用 runWithContext 来恢复上下文,类似于 <script setup> 的工作方式。

¥This is where runWithContext can be used to restore context, similarly to how <script setup> works.

Nuxt 内部使用 unjs/unctx 来支持类似于 Vue 的插件和中间件组合。这使得像 navigateTo() 这样的可组合项无需直接将 nuxtApp 传递给它们即可工作。 - 将 Composition API 的 DX 和性能优势带到整个 Nuxt 框架。

¥Nuxt internally uses unjs/unctx to support composables similar to Vue for plugins and middleware. This enables composables like navigateTo() to work without directly passing nuxtApp to them - bringing the DX and performance benefits of Composition API to the whole Nuxt framework.

Nuxt 可组合项的设计与 Vue Composition API 相同,因此需要类似的解决方案才能神奇地完成这种转换。查看 unjs/unctx#2(提案)、unjs/unctx#4(转换实现)和 nuxt/framework#3884(集成到 Nuxt)。

¥Nuxt composables have the same design as the Vue Composition API and therefore need a similar solution to magically do this transform. Check out unjs/unctx#2 (proposal), unjs/unctx#4 (transform implementation), and nuxt/framework#3884 (Integration to Nuxt).

Vue 目前仅支持 <script setup> 的异步上下文恢复,以便使用 async/await。Nuxt 添加了对 defineNuxtPlugin()defineNuxtRouteMiddleware() 的转换支持,这意味着当你使用它们时,Nuxt 会自动通过上下文恢复进行转换。

¥Vue currently only supports async context restoration for <script setup> for async/await usage. In Nuxt, the transform support for defineNuxtPlugin() and defineNuxtRouteMiddleware() was added, which means when you use them Nuxt automatically transforms them with context restoration.

剩余问题

¥Remaining Issues

unjs/unctx 自动恢复上下文的转换似乎存在错误,因为 try/catch 语句包含 await,最终需要解决这个问题才能消除上述建议的解决方法的要求。

¥The unjs/unctx transformation to automatically restore context seems buggy with try/catch statements containing await which ultimately needs to be solved in order to remove the requirement of the workaround suggested above.

原生异步上下文

¥Native Async Context

使用一项新的实验性功能,可以使用 Node.js AsyncLocalStorage 和新的 unctx 支持启用原生异步上下文支持,从而使异步上下文原生可用于任何嵌套的异步可组合项,而无需进行转换或手动传递/调用上下文。

¥Using a new experimental feature, it is possible to enable native async context support using Node.js AsyncLocalStorage and new unctx support to make async context available natively to any nested async composable without needing a transform or manual passing/calling with context.

Bun 和 Node 目前支持原生异步上下文。
Read more in Docs > Guide > Going Further > Experimental Features#asynccontext.

tryUseNuxtApp

此函数的工作原理与 useNuxtApp 完全相同,但如果上下文不可用,则返回 null,而不是抛出异常。

¥This function works exactly the same as useNuxtApp, but returns null if context is unavailable instead of throwing an exception.

你可以将其用于不需要 nuxtApp 的可组合项,或者简单地检查上下文是否可用且不产生异常。

¥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:

composable.ts
export function useStandType() {
  // Always works on the client
  if (tryUseNuxtApp()) {
    return useRuntimeConfig().public.STAND_TYPE
  } else {
    return process.env.STAND_TYPE
  }
}