useNuxtApp
useNuxtApp — встроенный композабл для доступа к общему runtime-контексту Nuxt (контекст Nuxt). Доступен на клиенте и сервере (но не внутри маршрутов Nitro). Через него доступны экземпляр Vue-приложения, runtime-хуки, переменные runtime config и внутреннее состояние (ssrContext, payload).
<script setup lang="ts">
const nuxtApp = useNuxtApp()
</script>
Если контекст недоступен, useNuxtApp выбросит исключение. В композаблах, где nuxtApp не обязателен, или для проверки наличия контекста без исключения используйте tryUseNuxtApp.
Методы
provide (name, value)
nuxtApp — runtime-контекст, который можно расширять плагинами Nuxt. Функция provide создаёт плагины и делает значения и методы доступными во всём приложении.
provide принимает параметры name и value.
const nuxtApp = useNuxtApp()
nuxtApp.provide('hello', name => `Hello ${name}!`)
// Выведет "Hello name!"
console.log(nuxtApp.$hello('name'))
В примере выше $hello становится частью контекста nuxtApp и доступен везде, где доступен nuxtApp.
hook(name, cb)
Хуки nuxtApp позволяют настраивать runtime-поведение приложения. Их можно использовать в композаблах Vue и плагинах Nuxt, чтобы встроиться в жизненный цикл рендеринга.
Функция hook добавляет логику в определённую точку жизненного цикла. Чаще всего используется при создании плагинов.
Список runtime-хуков: Runtime Hooks.
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('page:start', () => {
/* ваш код */
})
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, доступный через nuxtApp.
Полезные методы:
component()— регистрация глобального компонента или получение уже зарегистрированного.directive()— регистрация глобальной директивы или получение уже зарегистрированной (пример).use()— установка плагина Vue (пример).
ssrContext
ssrContext создаётся при SSR и доступен только на сервере.
Через ssrContext доступны:
url(string) — URL текущего запроса.event(h3) — объект запроса и ответа текущего маршрута.payload(object) — объект payload NuxtApp.
payload
payload передаёт данные и состояние с сервера на клиент. На клиенте после передачи доступны:
serverRendered(boolean) — ответ отрендерен на сервере.data(object) — данные, полученные черезuseFetchилиuseAsyncData, доступны в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 } })
После загрузкиcountчерезuseAsyncDataвpayload.dataбудет запись{ count: 1 }.
Тот жеpayload.dataдоступен на сервере черезssrContext.state(object) — общее состояние, заданное композабломuseState, доступно вpayload.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
С Nuxt v3.4 можно задать свой reducer/reviver для типов, не поддерживаемых Nuxt.
В примере ниже заданы reducer (сериализатор) и reviver (десериализатор) для класса Luxon DateTime через payload-плагин.
/**
* Такой плагин выполняется очень рано, до восстановления payload.
* Роутер и другие свойства Nuxt ещё недоступны.
* Строка "DateTime" — идентификатор типа, должна совпадать в reducer и 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) — идёт ли гидрация приложения на клиенте.
export default defineComponent({
setup (_props, { slots, emit }) {
const nuxtApp = useNuxtApp()
onErrorCaptured((err) => {
if (import.meta.client && !nuxtApp.isHydrating) {
// ...
}
})
},
})
runWithContext
runWithContext вызывает функцию с явно переданным контекстом Nuxt. Обычно контекст передаётся неявно, но в сложных сценариях с async/await в middleware/плагинах контекст может сброситься после асинхронного вызова.
export default defineNuxtRouteMiddleware(async (to, from) => {
const nuxtApp = useNuxtApp()
let user
try {
user = await fetchUser()
// контекст Vue/Nuxt теряется здесь из-за try/catch
} catch (e) {
user = null
}
if (!user) {
// восстанавливаем контекст Nuxt для вызова navigateTo
return nuxtApp.runWithContext(() => navigateTo('/auth'))
}
})
Использование
const result = nuxtApp.runWithContext(() => functionWithContext())
functionWithContext: функция, которой нужен контекст текущего приложения Nuxt. Контекст подставится автоматически.
runWithContext возвращает результат вызова functionWithContext.
Подробнее о контексте
Composition API Vue и композаблы Nuxt опираются на неявный контекст. Vue сохраняет текущий экземпляр компонента (а Nuxt — nuxtApp) в глобальную переменную и сбрасывает её в том же тике. На сервере обрабатываются запросы разных пользователей в одном глобальном контексте, поэтому Nuxt и Vue сбрасывают экземпляр сразу после использования, чтобы не утекала общая ссылка.
Итог: Composition API и композаблы Nuxt доступны только в рамках жизненного цикла и до первого await:
// --- Vue internal ---
const _vueInstance = null
const getCurrentInstance = () => _vueInstance
// ---
// Vue/Nuxt записывает текущий компонент в _vueInstance при вызове setup()
async function setup () {
getCurrentInstance() // работает
await someAsyncOperation() // перед await контекст сбрасывается!
getCurrentInstance() // null
}
Классическое решение — сохранить экземпляр в переменную при первом вызове (const instance = getCurrentInstance()), но тогда вложенные композаблы должны принимать экземпляр явно. Это ограничение дизайна, а не баг.
В <script setup> Vue при компиляции подставляет восстановление контекста после асинхронных вызовов:
const __instance = getCurrentInstance() // Generated by Vue compiler
getCurrentInstance() // работает
await someAsyncOperation() // контекст сброшен
__restoreInstance(__instance) // генерирует компилятор Vue
getCurrentInstance() // снова работает
Подробнее о том, как Vue восстанавливает контекст: unjs/unctx#2 (comment).
Решение
runWithContext восстанавливает контекст вручную, по аналогии с тем, как это делает <script setup>.
Nuxt использует unjs/unctx для поддержки композаблов в плагинах и middleware. Благодаря этому navigateTo() и другие работают без явной передачи nuxtApp. Подробнее: unjs/unctx#2, unjs/unctx#4, nuxt/framework#3884.
Vue восстанавливает контекст только в <script setup>. В Nuxt такая трансформация добавлена для defineNuxtPlugin() и defineNuxtRouteMiddleware() — при их использовании контекст восстанавливается автоматически.
Известные проблемы
Трансформация unctx для авто-восстановления контекста некорректно ведёт себя с try/catch, внутри которого есть await — в таких случаях нужен ручной вызов runWithContext.
Нативный async-контекст
Экспериментальная возможность — Node.js AsyncLocalStorage и поддержка в unctx: async-контекст доступен нативно в любых вложенных асинхронных композаблах без трансформации и без ручной передачи контекста.
tryUseNuxtApp
Работает так же, как useNuxtApp, но при недоступном контексте возвращает null вместо исключения.
Подходит для композаблов, которым не обязателен nuxtApp, или для проверки наличия контекста без исключения.
Пример:
export function useStandType () {
// На клиенте всегда есть контекст
if (tryUseNuxtApp()) {
return useRuntimeConfig().public.STAND_TYPE
} else {
return process.env.STAND_TYPE
}
}