Skip to content

NuxtApp

NuxtApp 是 Nuxt 应用的运行时实例,可以在组件、插件和组合式函数中访问。

大多数情况下你不需要直接使用 useNuxtApp()

它主要用于插件开发和高级场景(如跨上下文调用、访问 SSR Payload)。日常开发中,useFetchuseStateuseRouter 等组合式函数已经封装了 NuxtApp 的能力。

useNuxtApp

ts
const nuxtApp = useNuxtApp()

INFO

调用限制: useNuxtApp() 只能在 setup() 函数、插件、组合式函数中调用 不能在普通函数、定时器回调、异步回调中直接调用(会丢失上下文)

核心属性

属性类型说明
vueAppAppVue 应用实例
payloadobjectSSR Payload(服务端→客户端数据传递)
isHydratingboolean是否正在 Hydration
ssrContextSSRContextSSR 上下文(仅服务端可用)
configRuntimeConfig运行时配置

为什么服务端每次请求都是新实例?

INFO

这是 Nuxt 设计的核心安全机制 如果服务端使用同一个 NuxtApp 实例处理多个请求,用户 A 的数据可能泄露给用户 B——这就是"跨请求状态污染"

ts
// ❌ 危险:在模块顶层存储共享状态(服务端所有请求共享同一个变量)
const sharedData = ref({})

// ✅ 安全:使用 useState,每次请求都是独立的
const data = useState('data', () => ({}))
环境NuxtApp 实例状态隔离
服务端每次请求创建新实例✅ 请求间隔离
客户端单例(只创建一次)✅ 页面间共享

Payload

Payload 是 Nuxt 实现 SSR 数据传递的核心机制——服务端获取的数据序列化后嵌入 HTML,客户端直接使用,避免重复请求:

ts
nuxtApp.payload.data       // useFetch/useAsyncData 缓存
nuxtApp.payload.state      // useState 缓存
nuxtApp.payload.error      // 全局错误
nuxtApp.payload._errors    // 错误集合

INFO

控制 Payload 大小! Payload 会内联到 HTML 中 体积过大会导致首屏加载变慢。使用 useFetchpicktransform 选项减少传递的数据量

provide / inject

插件中 provide

ts
// 插件
export default defineNuxtPlugin(() => {
  return {
    provide: {
      api: $fetch.create({ baseURL: '/api' }),
      formatDate: (date: string) => new Date(date).toLocaleDateString(),
    },
  }
})

使用 inject

ts
const { $api, $formatDate } = useNuxtApp()

为什么 provide 的变量名以 $ 开头?

这是约定,避免与 Vue 内部属性冲突。Nuxt 会自动添加 $ 前缀,所以 provide: { api } 会变成 $api

类型扩展

ts
// 使 TypeScript 能识别 $api 和 $formatDate
declare module '#app' {
  interface NuxtApp {
    $api: typeof $fetch
    $formatDate: (date: string) => string
  }
}
export {}

钩子

ts
const nuxtApp = useNuxtApp()

// 应用就绪(仅客户端)
nuxtApp.hook('app:mounted', () => {
  console.log('App mounted')
})

// 页面加载开始(适合显示加载条)
nuxtApp.hook('page:start', () => {})

// 页面加载完成(适合隐藏加载条)
nuxtApp.hook('page:finish', () => {})

// Vue 错误(适合上报到 Sentry)
nuxtApp.hook('vue:error', (error) => {})

// 应用错误
nuxtApp.hook('app:error', (error) => {})

TIP

nuxtApp.hook() 返回一个取消监听函数:const unsubscribe = nuxtApp.hook(...) 在不需要时调用 unsubscribe()

runWithContext

为什么需要 runWithContext

在异步回调(setTimeout、Promise 回调等)中,Nuxt 上下文会丢失,导致 useFetchuseState 等组合式函数报错。runWithContext 让你重新进入 Nuxt 上下文。

ts
// ❌ 错误:异步回调中丢失上下文
setTimeout(() => {
  const data = useFetch('/api/data')  // 报错!上下文已丢失
}, 1000)

// ✅ 正确:使用 runWithContext 恢复上下文
setTimeout(async () => {
  const result = await nuxtApp.runWithContext(() => {
    return useFetch('/api/data')  // OK
  })
}, 1000)

典型场景:在 WebSocket 回调、第三方库回调中使用 Nuxt 组合式函数。

callOnce

确保函数只在当前 NuxtApp 实例中执行一次(跨 SSR 和客户端也不重复执行):

ts
await nuxtApp.callOnce('init', async () => {
  await initializeApp()
})

典型场景:防止重复初始化第三方库(如 Google Analytics、WebSocket 连接)。

常见问题

问题原因解决方案
useNuxtApp is not a function在异步回调中调用使用 runWithContext
跨请求数据泄露在模块顶层存储共享状态使用 useState 代替模块级 ref
Payload 过大导致首屏慢useFetch 返回了过多数据使用 pick/transform 减少数据量
插件间循环依赖插件 A 引用插件 B 的 provide,B 又引用 A合并为一个插件,或使用事件通信
provide 的值客户端读不到.server.ts 插件 provide 的值不会发送到客户端改用 .ts 插件,或通过 useState 传递

基于 Nuxt 4 官方文档整理编写