useNuxtApp

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

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

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

如果在你的作用域中无法获取运行时上下文,调用 useNuxtApp 将抛出异常。你可以使用 tryUseNuxtApp 替代,以用于不需要 nuxtApp 的可组合函数,或仅检查上下文是否可用而不抛出异常。

方法

provide (name, value)

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

provide 函数接受 namevalue 参数。

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

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

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

hook(name, cb)

nuxtApp 中可用的钩子允许你自定义 Nuxt 应用的运行时行为。你可以在 Vue.js 可组合函数和 Nuxt 插件 中使用运行时钩子来挂钩到渲染生命周期。

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

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

plugins/test.ts
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hook('page:start', () => {
    /* 你的代码写在这里 */
  })
  nuxtApp.hook('vue:error', (..._args) => {
    console.log('vue:error')
    // if (import.meta.client) {
    //   console.log(..._args)
    // }
  })
})

callHook(name, ...args)

callHook 在调用任何现有钩子时返回一个 Promise。

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

属性

useNuxtApp() 暴露以下属性,你可以用来扩展和自定义应用以及共享状态、数据和变量。

vueApp

vueApp 是通过 nuxtApp 可访问的全局 Vue.js 应用实例

一些常用方法:

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

ssrContext

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

Nuxt 通过 ssrContext 暴露以下属性:

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

payload

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

  • serverRendered (boolean) - 指示响应是否为服务器端渲染。
  • data (object) - 当你使用 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 (object) - 当你在 Nuxt 中使用 useState 可组合函数设置共享状态时,此状态数据通过 payload.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(或反序列化器),使用负载插件。
    plugins/date-time-payload.ts
    /**
     * 这种插件在 Nuxt 生命周期中很早运行,在恢复负载之前。
     * 你无法访问路由器或其他 Nuxt 注入的属性。
     *
     * 注意,“DateTime” 字符串是类型标识符,必须
     * 在 reducer 和 reviver 中保持相同。
     */
    export default definePayloadPlugin((nuxtApp) => {
      definePayloadReducer('DateTime', (value) => {
        return value instanceof DateTime && value.toJSON()
      })
      definePayloadReviver('DateTime', (value) => {
        return DateTime.fromISO(value)
      })
    })
    

isHydrating

使用 nuxtApp.isHydrating (boolean) 检查 Nuxt 应用是否在客户端进行水合。

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 实例不可用”消息而来到这里。请谨慎使用此方法,并报告导致问题的示例,以便在框架层面最终解决问题。

runWithContext 方法用于调用一个函数并为其提供明确的 Nuxt 上下文。通常,Nuxt 上下文是隐式传递的,你无需担心。然而,在处理复杂的 async/await 场景(例如中间件/插件)时,你可能会遇到在异步调用后当前实例被取消设置的情况。

middleware/auth.ts
export default defineNuxtRouteMiddleware(async (to, from) => {
  const nuxtApp = useNuxtApp()
  let user
  try {
    user = await fetchUser()
    // 由于 try/catch 块,Vue/Nuxt 编译器在此处丢失上下文。
  } catch (e) {
    user = null
  }
  if (!user) {
    // 为我们的 `navigateTo` 调用应用正确的 Nuxt 上下文。
    return nuxtApp.runWithContext(() => navigateTo('/auth'))
  }
})

使用方法

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

runWithContext 将返回 functionWithContext 返回的任何内容。

上下文的深入解释

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

这意味着什么?组合 API 和 Nuxt 可组合函数仅在生命周期内且在任何异步操作前的同一 tick 内可用:

// --- Vue 内部 ---
const _vueInstance = null
const getCurrentInstance = () => _vueInstance
// ---

// Vue / Nuxt 在调用 setup() 时将当前组件的全局变量引用设置为 _vueInstance
async function setup() {
  getCurrentInstance() // 有效
  await someAsyncOperation() // Vue 在异步操作前取消上下文设置!
  getCurrentInstance() // null
}

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

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

const __instance = getCurrentInstance() // 由 Vue 编译器生成
getCurrentInstance() // 有效!
await someAsyncOperation() // Vue 取消上下文设置
__restoreInstance(__instance) // 由 Vue 编译器生成
getCurrentInstance() // 仍然有效!

有关 Vue 实际操作的更好描述,请参见 unjs/unctx#2 (comment)

解决方案

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

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

Nuxt 可组合函数与 Vue 组合 API 具有相同的设计,因此需要类似的解决方案来自动进行这种转换。查看 unjs/unctx#2(提案)、unjs/unctx#4(转换实现)和 nuxt/framework#3884(Nuxt 集成)。

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

剩余问题

unjs/unctx 转换在包含 awaittry/catch 语句中似乎存在问题,最终需要解决以消除上述解决方法的需要。

原生异步上下文

使用新的实验性功能,可以通过 Node.js AsyncLocalStorage 和新的 unctx 支持启用原生异步上下文支持,使异步上下文原生地对任何嵌套异步可组合函数可用,无需转换或手动传递/调用上下文。

原生异步上下文支持当前在 Bun 和 Node 中有效。
阅读更多 Docs > Guide > Going Further > Experimental Features#asynccontext.

tryUseNuxtApp

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

你可以在不需要 nuxtApp 的可组合函数中使用它,或仅检查上下文是否可用而不抛出异常。

示例用法:

composable.ts
export function useStandType() {
  // 在客户端上始终有效
  if (tryUseNuxtApp()) {
    return useRuntimeConfig().public.STAND_TYPE
  } else {
    return process.env.STAND_TYPE
  }
}