useNuxtApp

源文件
访问 Nuxt 应用程序的共享运行时上下文。

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

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

如果在当前作用域中无法获取运行时上下文,调用 useNuxtApp 将会抛出异常。您可以改用 tryUseNuxtApp 来处理不需要 nuxtApp 的 composable,或者仅在不抛出异常的情况下检查上下文是否可用。

方法

provide (name, value)

nuxtApp 是一个您可以使用 Nuxt 插件 扩展的运行时上下文。使用 provide 函数创建 Nuxt 插件,使值和辅助方法可在整个 Nuxt 应用程序的 composables 和组件中访问。

provide 函数接受 namevalue 参数。

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

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

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

hook(name, cb)

nuxtApp 中可用的钩子允许您自定义 Nuxt 应用程序的运行时方面。您可以在 Vue.js composables 和 Nuxt 插件 中使用运行时钩子来挂接到渲染生命周期。

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

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

app/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。

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

属性

useNuxtApp() 暴露了以下属性,可用于扩展和自定义您的应用程序以及共享状态、数据和变量。

vueApp

vueApp 是全局 Vue.js应用程序实例您可以通过 nuxtApp 访问它。

一些有用的方法

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

ssrContext

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

Nuxt 通过 ssrContext 暴露以下属性

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

payload

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

  • serverRendered (boolean) - 指示响应是否经过服务器端渲染。
  • data (object) - 当您使用 <">useFetch<">useAsyncData 从 API 端点获取数据时,结果的 payload 可通过 payload.data 访问。此数据会缓存,有助于防止多次执行相同请求时重复获取相同数据。
    <script setup lang="ts">
    const { data } = await useAsyncData('count', (_nuxtApp, { signal }) => $fetch('/api/count', { signal }))
    </script>
    

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

    也可以使用更高级的类型,例如 < refreactiveshallowRefshallowReactive 和 < NuxtError

自定义 Reducer/Reviver

Nuxt v3.4以来,可以为 Nuxt 不支持的类型定义自己的 reducer/reviver。

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

<>app/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 (boolean) 检查 Nuxt 应用是否正在客户端水合(hydrating)。

<>app/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 场景时,您可能会遇到在异步调用后当前实例被取消设置的情况。

app/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'))
  }
})

使用

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

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

上下文的深入解释

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

这意味着什么?Composition API 和 Nuxt Composables 仅在生命周期内以及在任何异步操作之前的同一 tick 中可用

// --- 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(),并在下一个 composable 调用中使用它,但问题是任何嵌套的 composable 调用现在都需要显式地将实例作为参数接受,而不是依赖于 composition-api 的隐式上下文。这是 composables 的设计限制,本身不是问题。

为了克服这个限制,Vue 在编译我们的应用程序代码时会做一些幕后工作,并在每次调用 < <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 (comment).

解决方案

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

Nuxt 内部使用unjs/unctx以支持类似于 Vue 的插件和中间件 composables。这使得 < navigateTo() 等 composables 可以在不将 < nuxtApp 直接传递给它们的情况下工作——为整个 Nuxt 框架带来了 Composition API 的 DX 和性能优势。

Nuxt composables 具有与 Vue Composition API 相同的设计,因此需要类似的解决方案来神奇地完成此转换。请查看unjs/unctx#2(proposal),unjs/unctx#4(transform implementation), andnuxt/framework#3884(Integration to Nuxt)。

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

剩余问题

为了自动恢复上下文而对 < unjs/unctx 进行的转换似乎与包含 < await 的 < try/catch 语句存在错误,这最终需要在解决,以便消除上述建议的变通方法的需要。

原生异步上下文

使用一个新的实验性功能,可以通过Node.js < AsyncLocalStorage和 unctx 对其的新支持来启用原生异步上下文支持,以便**原生**地使异步上下文可用于**任何嵌套的异步 composable**,而无需进行转换或手动传递/调用上下文。

原生异步上下文支持目前在 Bun 和 Node 中工作。
在 < 文档 > 4 X > 指南 > 更进一步 > 实验性功能#asynccontext 中阅读更多内容。

tryUseNuxtApp

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

您可以将其用于不需要 < nuxtApp 的 composable,或者仅在不抛出异常的情况下检查上下文是否可用。

使用示例

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