useNuxtApp
useNuxtApp
是一个内置可组合组件,它提供了一种访问 Nuxt 共享运行时上下文(也称为 Nuxt 上下文)的方法,该上下文在客户端和服务器端均可用(但不在 Nitro 路由内)。它帮助你访问 Vue 应用实例、运行时钩子、运行时配置变量和内部状态,例如 ssrContext
和 payload
。
¥useNuxtApp
is a built-in composable that provides a way to access shared runtime context of Nuxt, also known as the Nuxt context, which is available on both client and server side (but not within Nitro routes). It helps you access the Vue app instance, runtime hooks, runtime config variables and internal states, such as ssrContext
and payload
.
<script setup lang="ts">
const nuxtApp = useNuxtApp()
</script>
如果你的作用域中没有可用的运行时上下文,调用 useNuxtApp
时将抛出异常。你可以将 tryUseNuxtApp
用于不需要 nuxtApp
的可组合项,或者简单地检查上下文是否可用(无异常)。
¥If runtime context is unavailable in your scope, useNuxtApp
will throw an exception when called. You can use tryUseNuxtApp
instead for composables that do not require nuxtApp
, or to simply check if context is available or not without an exception.
方法
¥Methods
provide (name, value)
nuxtApp
是一个运行时上下文,你可以使用 Nuxt 插件 进行扩展。使用 provide
函数创建 Nuxt 插件,使值和辅助方法在你的 Nuxt 应用中可用于所有可组合项和组件。
¥nuxtApp
is a runtime context that you can extend using Nuxt plugins. Use the provide
function to create Nuxt plugins to make values and helper methods available in your Nuxt application across all composables and components.
provide
函数接受 name
和 value
参数。
¥provide
function accepts name
and value
parameters.
const nuxtApp = useNuxtApp()
nuxtApp.provide('hello', (name) => `Hello ${name}!`)
// Prints "Hello name!"
console.log(nuxtApp.$hello('name'))
如上例所示,$hello
已成为 nuxtApp
上下文中新的自定义部分,并且在所有可访问 nuxtApp
的地方都可用。
¥As you can see in the example above, $hello
has become the new and custom part of nuxtApp
context and it is available in all places where nuxtApp
is accessible.
hook(name, cb)
nuxtApp
中提供的钩子允许你自定义 Nuxt 应用的运行时方面。你可以使用 Vue.js 可组合项和 Nuxt 插件 中的运行时钩子来连接到渲染生命周期。
¥Hooks available in nuxtApp
allows you to customize the runtime aspects of your Nuxt application. You can use runtime hooks in Vue.js composables and Nuxt plugins to hook into the rendering lifecycle.
hook
函数有助于通过在特定点挂载到渲染生命周期来添加自定义逻辑。hook
函数主要用于创建 Nuxt 插件。
¥hook
function is useful for adding custom logic by hooking into the rendering lifecycle at a specific point. hook
function is mostly used when creating Nuxt plugins.
有关 Nuxt 调用的可用运行时钩子,请参阅 运行时钩子。
¥See Runtime Hooks for available runtime hooks called by 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。
¥callHook
returns a promise when called with any of the existing hooks.
await nuxtApp.callHook('my-plugin:init')
属性
¥Properties
useNuxtApp()
公开以下属性,你可以使用这些属性来扩展和自定义你的应用,并共享状态、数据和变量。
¥useNuxtApp()
exposes the following properties that you can use to extend and customize your app and share state, data and variables.
vueApp
vueApp
是全局的 Vue.js 应用实例,你可以通过 nuxtApp
访问它。
¥vueApp
is the global Vue.js application instance that you can access through nuxtApp
.
一些实用方法:
¥Some useful methods:
component()
- 如果同时传递名称字符串和组件定义,则注册全局组件;如果仅传递名称,则检索已注册的组件。directive()
- 如果同时传递名称字符串和指令定义,则注册全局自定义指令;如果仅传递名称,则检索已注册的指令 (例子)。use()
- 安装 Vue.js 插件 (例子)。
ssrContext
ssrContext
是在服务器端渲染期间生成的,并且仅在服务器端可用。
¥ssrContext
is generated during server-side rendering and it is only available on the server side.
Nuxt 通过 ssrContext
公开以下属性:
¥Nuxt exposes the following properties through ssrContext
:
url
(字符串) - 当前请求 URL。event
(unjs/h3 请求事件) - 访问当前路由的请求和响应。payload
(对象) - NuxtApp 负载对象。
payload
payload
将数据和状态变量从服务器端暴露给客户端。以下键从服务器端传递后,将在客户端可用:
¥payload
exposes data and state variables from server side to client side. The following keys will be available on the client after they have been passed from the server side:
serverRendered
(布尔值) - 指示响应是否在服务器端渲染。data
(对象) - 当你使用useFetch
或useAsyncData
从 API 端点获取数据时,可以从payload.data
访问生成的有效负载。这些数据会被缓存,有助于避免在多次发出相同请求时获取相同的数据。<script setup lang="ts"> const { data } = await useAsyncData('count', () => $fetch('/api/count')) </script>
export default defineEventHandler(event => { return { count: 1 } })
在上面的示例中,使用useAsyncData
获取count
的值后,如果你访问payload.data
,你将看到{ count: 1 }
记录在那里。
当从ssrcontext
访问同一个payload.data
时,你也可以在服务器端访问相同的值。state
(对象) - 当你在 Nuxt 中使用useState
可组合项设置共享状态时,此状态数据是通过payload.state.[name-of-your-state]
访问的。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
。
自 Nuxt v3.4 起,你可以为 Nuxt 不支持的类型定义自己的 reducer/reviver。
在下面的示例中,我们使用有效负载插件为 Luxon DateTime 类定义了一个 reducer(或序列化器)和一个 reviver(或反序列化器)。
¥In the example below, we define a reducer (or a serializer) and a reviver (or deserializer) for the Luxon DateTime class, using a payload plugin.
/**
* 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
(布尔值)检查 Nuxt 应用是否在客户端进行 hydration。
¥Use nuxtApp.isHydrating
(boolean) to check if the Nuxt app is hydrating on the client side.
export default defineComponent({
setup (_props, { slots, emit }) {
const nuxtApp = useNuxtApp()
onErrorCaptured((err) => {
if (import.meta.client && !nuxtApp.isHydrating) {
// ...
}
})
}
})
runWithContext
runWithContext
方法旨在用于调用函数并赋予其显式的 Nuxt 上下文。通常情况下,Nuxt 上下文是隐式传递的,你无需担心这一点。但是,在中间件/插件中处理复杂的 async
/await
场景时,你可能会遇到异步调用后当前实例被取消设置的情况。
¥The runWithContext
method is meant to be used to call a function and give it an explicit Nuxt context. Typically, the Nuxt context is passed around implicitly and you do not need to worry about this. However, when working with complex async
/await
scenarios in middleware/plugins, you can run into instances where the current instance has been unset after an async call.
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'))
}
})
用法
¥Usage
const result = nuxtApp.runWithContext(() => functionWithContext())
functionWithContext
:任何需要当前 Nuxt 应用上下文的函数。此上下文将自动正确应用。
runWithContext
将返回 functionWithContext
返回的内容。
¥runWithContext
will return whatever is returned by functionWithContext
.
上下文的深度解释
¥A Deeper Explanation of Context
Vue.js Composition API(以及类似 Nuxt 可组合项)依赖于隐式上下文工作。在生命周期中,Vue 会将当前组件的临时实例(以及 Nuxt App 的 Nuxt 临时实例)设置为全局变量,并在同一 tick 中将其取消设置。在服务器端渲染时,会有来自不同用户的多个请求,nuxtApp 运行在同一个全局上下文中。因此,Nuxt 和 Vue 会立即取消设置此全局实例,以避免在两个用户或组件之间泄漏共享引用。
¥Vue.js Composition API (and Nuxt composables similarly) work by depending on an implicit context. During the lifecycle, Vue sets the temporary instance of the current component (and Nuxt temporary instance of nuxtApp) to a global variable and unsets it in same tick. When rendering on the server side, there are multiple requests from different users and nuxtApp running in a same global context. Because of this, Nuxt and Vue immediately unset this global instance to avoid leaking a shared reference between two users or components.
它的含义是什么?Composition API 和 Nuxt Composables 仅在生命周期内以及任何异步操作之前的同一 tick 中可用:
¥What it does mean? The Composition API and Nuxt Composables are only available during lifecycle and in same tick before any async operation:
// --- 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()
),并在下一次可组合调用中使用它,但问题是任何嵌套的可组合调用现在都需要显式接受该实例作为参数,而不是依赖于 Composition-API 的隐式上下文。这是可组合组件的设计限制,本身并非问题。
¥The classic solution to this, is caching the current instance on first call to a local variable like const instance = getCurrentInstance()
and use it in the next composable call but the issue is that any nested composable calls now needs to explicitly accept the instance as an argument and not depend on the implicit context of composition-api. This is design limitation with composables and not an issue per-se.
为了克服这一限制,Vue 在编译我们的应用代码时会进行一些后台工作,并在每次调用 <script setup>
后恢复上下文:
¥To overcome this limitation, Vue does some behind the scenes work when compiling our application code and restores context after each call for <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 (评论)。
¥For a better description of what Vue actually does, see unjs/unctx#2 (comment).
解决方案
¥Solution
在这里,可以使用 runWithContext
来恢复上下文,类似于 <script setup>
的工作方式。
¥This is where runWithContext
can be used to restore context, similarly to how <script setup>
works.
Nuxt 内部使用 unjs/unctx 来支持类似于 Vue 的插件和中间件组合。这使得像 navigateTo()
这样的可组合项无需直接将 nuxtApp
传递给它们即可工作。 - 将 Composition API 的 DX 和性能优势带到整个 Nuxt 框架。
¥Nuxt internally uses unjs/unctx to support composables similar to Vue for plugins and middleware. This enables composables like navigateTo()
to work without directly passing nuxtApp
to them - bringing the DX and performance benefits of Composition API to the whole Nuxt framework.
Nuxt 可组合项的设计与 Vue Composition API 相同,因此需要类似的解决方案才能神奇地完成这种转换。查看 unjs/unctx#2(提案)、unjs/unctx#4(转换实现)和 nuxt/framework#3884(集成到 Nuxt)。
¥Nuxt composables have the same design as the Vue Composition API and therefore need a similar solution to magically do this transform. Check out unjs/unctx#2 (proposal), unjs/unctx#4 (transform implementation), and nuxt/framework#3884 (Integration to Nuxt).
Vue 目前仅支持 <script setup>
的异步上下文恢复,以便使用 async/await。Nuxt 添加了对 defineNuxtPlugin()
和 defineNuxtRouteMiddleware()
的转换支持,这意味着当你使用它们时,Nuxt 会自动通过上下文恢复进行转换。
¥Vue currently only supports async context restoration for <script setup>
for async/await usage. In Nuxt, the transform support for defineNuxtPlugin()
and defineNuxtRouteMiddleware()
was added, which means when you use them Nuxt automatically transforms them with context restoration.
剩余问题
¥Remaining Issues
unjs/unctx
自动恢复上下文的转换似乎存在错误,因为 try/catch
语句包含 await
,最终需要解决这个问题才能消除上述建议的解决方法的要求。
¥The unjs/unctx
transformation to automatically restore context seems buggy with try/catch
statements containing await
which ultimately needs to be solved in order to remove the requirement of the workaround suggested above.
原生异步上下文
¥Native Async Context
使用一项新的实验性功能,可以使用 Node.js AsyncLocalStorage
和新的 unctx 支持启用原生异步上下文支持,从而使异步上下文原生可用于任何嵌套的异步可组合项,而无需进行转换或手动传递/调用上下文。
¥Using a new experimental feature, it is possible to enable native async context support using Node.js AsyncLocalStorage
and new unctx support to make async context available natively to any nested async composable without needing a transform or manual passing/calling with context.
tryUseNuxtApp
此函数的工作原理与 useNuxtApp
完全相同,但如果上下文不可用,则返回 null
,而不是抛出异常。
¥This function works exactly the same as useNuxtApp
, but returns null
if context is unavailable instead of throwing an exception.
你可以将其用于不需要 nuxtApp
的可组合项,或者简单地检查上下文是否可用且不产生异常。
¥You can use it for composables that do not require nuxtApp
, or to simply check if context is available or not without an exception.
使用示例:
¥Example usage:
export function useStandType() {
// Always works on the client
if (tryUseNuxtApp()) {
return useRuntimeConfig().public.STAND_TYPE
} else {
return process.env.STAND_TYPE
}
}