useNuxtApp
useNuxtApp 是一个内置的组合式函数,它提供了一种访问 Nuxt 共享运行时上下文(也称为 Nuxt 上下文)的方法,该上下文在客户端和服务端均可用(但在 Nitro 路由中不可用)。它可帮助你访问 Vue 应用实例、运行时钩子、运行时配置变量以及诸如 ssrContext 和 payload 等内部状态。
<script setup lang="ts">
const nuxtApp = useNuxtApp()
</script>
如果在你的作用域中无法获取运行时上下文,调用 useNuxtApp 时将抛出异常。对于不需要 nuxtApp 的组合式函数,或者为了在不抛出异常的情况下简单检查上下文是否可用,你可以改用 tryUseNuxtApp。
方法
provide (name, value)
nuxtApp 是一个运行时上下文,你可以使用 Nuxt 插件对其进行扩展。使用 provide 函数创建 Nuxt 插件,以便在 Nuxt 应用的所有组合式函数和组件中提供值和辅助方法。
provide 函数接受 name 和 value 参数。
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 组合式函数和 Nuxt 插件中使用运行时钩子来接入渲染生命周期。
hook 函数可用于通过在渲染生命周期的特定点注入钩子来添加自定义逻辑。hook 函数主要用于创建 Nuxt 插件时。
请参阅 运行时钩子 以了解由 Nuxt 调用的可用运行时钩子。
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 插件 (示例)。
ssrContext
ssrContext 在服务端渲染期间生成,且仅在服务端可用。
Nuxt 通过 ssrContext 暴露了以下属性
url(string) - 当前请求的 URL。event(h3js/h3 请求事件) - 访问当前路由的请求和响应。payload(object) - NuxtApp 负载对象。
payload
payload 将服务端的数据和状态变量暴露给客户端。从服务端传递后,以下键将在客户端可用:
serverRendered(boolean) - 指示响应是否为服务端渲染。data(object) - 当你使用useFetch或useAsyncData从 API 端点获取数据时,可以通过payload.data访问生成的结果负载。此数据会被缓存,可帮助你防止在发起多次相同的请求时重复获取相同的数据。<script setup lang="ts"> const { data } = await useAsyncData('count', (_nuxtApp, { signal }) => $fetch('/api/count', { signal })) </script>export default defineEventHandler((event) => { return { count: 1 } })
在上面的示例中使用useAsyncData获取count的值之后,如果你访问payload.data,你将看到其中记录了{ count: 1 }。
当从ssrcontext访问相同的payload.data时,你也可以在服务端访问相同的值。state(object) - 当你在 Nuxt 中使用useState组合式函数设置共享状态时,可以通过payload.state.[name-of-your-state]访问此状态数据。app/plugins/my-plugin.tsexport const useColor = () => useState<string>('color', () => 'pink') export default defineNuxtPlugin((nuxtApp) => { if (import.meta.server) { const color = useColor() } })
还可以使用更高级的类型,例如ref、reactive、shallowRef、shallowReactive和NuxtError。
自定义 Reducer/Reviver v3.4
自 Nuxt v3.4 起,你可以为 Nuxt 不支持的类型定义你自己的 reducer/reviver。
在下面的示例中,我们使用负载插件为 Luxon DateTime 类定义了一个 reducer(或序列化器)和一个 reviver(或反序列化器)。
/**
* 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 应用是否正在客户端进行水合。
export default defineComponent({
setup (_props, { slots, emit }) {
const nuxtApp = useNuxtApp()
onErrorCaptured((err) => {
if (import.meta.client && !nuxtApp.isHydrating) {
// ...
}
})
},
})
runWithContext
runWithContext 方法旨在用于调用一个函数并为其赋予显式的 Nuxt 上下文。通常情况下,Nuxt 上下文是隐式传递的,你无需为此担心。然而,在中间件/插件中使用复杂的 async/await 场景时,你可能会遇到在异步调用后当前实例被取消设置的情况。
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 组合式 API(以及类似的 Nuxt 组合式函数)通过依赖隐式上下文来工作。在生命周期中,Vue 会将当前组件的临时实例(以及 Nuxt 的 nuxtApp 临时实例)设置为全局变量,并在同一 tick 中取消设置。当在服务端渲染时,会有来自不同用户的多个请求,并且 nuxtApp 在同一个全局上下文中运行。正因如此,Nuxt 和 Vue 会立即取消设置此全局实例,以避免在两个用户或组件之间泄漏共享引用。
这意味着什么?组合式 API 和 Nuxt 组合式函数仅在生命周期内以及在任何异步操作之前的同一 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(),并在接下来的组合式函数调用中使用它。但问题在于,任何嵌套的组合式函数调用现在都需要显式地将该实例作为参数传入,而不能依赖组合式 API 的隐式上下文。这是组合式函数的设计局限性,本身并非一个 Bug。
为了克服这一局限性,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 的组合式函数支持。这使得诸如 navigateTo() 之类的组合式函数能够在无需直接向其传递 nuxtApp 的情况下正常工作——从而将组合式 API 的开发体验和性能优势带给整个 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 转换在包含 await 的 try/catch 语句中似乎存在 Bug,为了最终消除对上述建议的变通方法的依赖,这一问题亟待解决。
原生异步上下文
使用一项新的实验性功能,可以通过 Node.js AsyncLocalStorage 和新的 unctx 支持来启用原生异步上下文支持,从而使异步上下文能够原生地提供给任何嵌套的异步组合式函数,而无需进行代码转换或手动传递/带上下文调用。
tryUseNuxtApp v3.10
此函数的工作方式与 useNuxtApp 完全相同,但如果上下文不可用,它会返回 null 而不是抛出异常。
你可以将其用于不需要 nuxtApp 的组合式函数,或者用于在不引发异常的情况下简单检查上下文是否可用。
使用示例
export function useStandType () {
// Always works on the client
if (tryUseNuxtApp()) {
return useRuntimeConfig().public.STAND_TYPE
} else {
return process.env.STAND_TYPE
}
}