NuxtApp
NuxtApp 是 Nuxt 应用的运行时实例,可以在组件、插件和组合式函数中访问。
大多数情况下你不需要直接使用 useNuxtApp()
它主要用于插件开发和高级场景(如跨上下文调用、访问 SSR Payload)。日常开发中,useFetch、useState、useRouter 等组合式函数已经封装了 NuxtApp 的能力。
useNuxtApp
const nuxtApp = useNuxtApp()INFO
️ 调用限制: useNuxtApp() 只能在 setup() 函数、插件、组合式函数中调用 不能在普通函数、定时器回调、异步回调中直接调用(会丢失上下文)
核心属性
| 属性 | 类型 | 说明 |
|---|---|---|
vueApp | App | Vue 应用实例 |
payload | object | SSR Payload(服务端→客户端数据传递) |
isHydrating | boolean | 是否正在 Hydration |
ssrContext | SSRContext | SSR 上下文(仅服务端可用) |
config | RuntimeConfig | 运行时配置 |
为什么服务端每次请求都是新实例?
INFO
这是 Nuxt 设计的核心安全机制 如果服务端使用同一个 NuxtApp 实例处理多个请求,用户 A 的数据可能泄露给用户 B——这就是"跨请求状态污染"
// ❌ 危险:在模块顶层存储共享状态(服务端所有请求共享同一个变量)
const sharedData = ref({})
// ✅ 安全:使用 useState,每次请求都是独立的
const data = useState('data', () => ({}))| 环境 | NuxtApp 实例 | 状态隔离 |
|---|---|---|
| 服务端 | 每次请求创建新实例 | ✅ 请求间隔离 |
| 客户端 | 单例(只创建一次) | ✅ 页面间共享 |
Payload
Payload 是 Nuxt 实现 SSR 数据传递的核心机制——服务端获取的数据序列化后嵌入 HTML,客户端直接使用,避免重复请求:
nuxtApp.payload.data // useFetch/useAsyncData 缓存
nuxtApp.payload.state // useState 缓存
nuxtApp.payload.error // 全局错误
nuxtApp.payload._errors // 错误集合INFO
️ 控制 Payload 大小! Payload 会内联到 HTML 中 体积过大会导致首屏加载变慢。使用 useFetch 的 pick 和 transform 选项减少传递的数据量
provide / inject
插件中 provide
// 插件
export default defineNuxtPlugin(() => {
return {
provide: {
api: $fetch.create({ baseURL: '/api' }),
formatDate: (date: string) => new Date(date).toLocaleDateString(),
},
}
})使用 inject
const { $api, $formatDate } = useNuxtApp()为什么 provide 的变量名以 $ 开头?
这是约定,避免与 Vue 内部属性冲突。Nuxt 会自动添加 $ 前缀,所以 provide: { api } 会变成 $api。
类型扩展
// 使 TypeScript 能识别 $api 和 $formatDate
declare module '#app' {
interface NuxtApp {
$api: typeof $fetch
$formatDate: (date: string) => string
}
}
export {}钩子
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 上下文会丢失,导致 useFetch、useState 等组合式函数报错。runWithContext 让你重新进入 Nuxt 上下文。
// ❌ 错误:异步回调中丢失上下文
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 和客户端也不重复执行):
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 传递 |