useNuxtApp

Исходный код
Доступ к общему контексту времени выполнения приложения Nuxt.

useNuxtApp — встроенный композабл для доступа к общему контексту выполнения Nuxt (контекст Nuxt) на клиенте и сервере (но не в маршрутах Nitro). Через него доступны экземпляр Vue-приложения, runtime-хуки, runtimeConfig и внутреннее состояние вроде ssrContext и payload.

app.vue
<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 => `Привет, ${name}!`)

// Выведет «Привет, мир!»
console.log(nuxtApp.$hello('мир'))

Как в примере выше, $hello становится частью nuxtApp и доступен везде, где есть контекст Nuxt.

hook(name, cb)

Хуки, доступные в nuxtApp, позволяют вам настраивать аспекты времени выполнения вашего Nuxt-приложения. Вы можете использовать runtime-хуки в композаблах Vue.js и Nuxt-плагинах, чтобы подключиться к жизненному циклу рендеринга.

hook подключает вашу логику к этапам рендеринга; чаще всего его вызывают из Nuxt-плагинов.

Смотрите хуки жизненного цикла для получения информации о доступных runtime-хуках, вызываемых Nuxt.

plugins/test.ts
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 возвращает промис при вызове любого из существующих хуков.

await nuxtApp.callHook('my-plugin:init')

Свойства

Функция useNuxtApp() открывает следующие свойства, которые вы можете использовать для расширения и настройки вашего приложения, а также для обмена состоянием, данными и переменными.

vueApp

vueApp — это глобальный экземпляр приложения Vue.js, к которому вы можете получить доступ через nuxtApp.

Некоторые полезные методы:

  • component() — Регистрирует глобальный компонент, если передаётся имя в виде строки и определение компонента, или извлекает уже зарегистрированный компонент, если передаётся только имя.
  • directive() — Регистрирует глобальную пользовательскую директиву, если передаётся имя в виде строки и определение директивы, или извлекает уже зарегистрированную, если передано только имя (пример).
  • use() — подключает плагин Vue (пример).
Узнать больше https://ru.vuejs.org/api/application.html#application-api.

ssrContext

ssrContext генерируется во время рендеринга на сервере и доступен только на сервере.

Nuxt предоставляет через ssrContext:

  • url (string) — URL текущего запроса;
  • event (объект события h3) — доступ к запросу и ответу;
  • payload (object) — объект payload приложения Nuxt.

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>
    

    После получения значения count с помощью useAsyncData в примере выше, если вы обратитесь к payload.data, вы увидите { count: 1 }, записанные в нём.
    Обращаясь к тем же payload.data из ssrContext, на сервере вы получите то же значение.
  • state (object) — при использовании useState общее состояние доступно как payload.state.<ключ-состояния>.
    plugins/my-plugin.ts
    export 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 не сериализует по умолчанию.

В примере ниже мы определяем редьюсер (или сериализатор) и ревайвер (или десериализатор) для класса Luxon DateTime, используя плагин полезной нагрузки.

plugins/date-time-payload.ts
/**
 * Такого рода плагины запускаются в самом начале жизненного цикла Nuxt, до того, как мы получим полезную нагрузку.
 * У вас не будет доступа к маршрутизатору или другим внедряемым свойствам Nuxt.
 *
 * Обратите внимание, что строка "DateTime" является идентификатором типа и должна
 * быть одинаковой как на редьюсере, так и на ревайвере.
 */
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 на клиенте.

components/nuxt-error-boundary.ts
export default defineComponent({
  setup (_props, { slots, emit }) {
    const nuxtApp = useNuxtApp()
    onErrorCaptured((err) => {
      if (import.meta.client && !nuxtApp.isHydrating) {
        // ...
      }
    })
  },
})

runWithContext

Скорее всего, вы попали сюда, потому что получили сообщение «Экземпляр Nuxt недоступен» (Nuxt instance unavailable). Пожалуйста, используйте этот метод осторожно и сообщайте о примерах, вызывающих проблемы, чтобы в конечном итоге их можно было решить на уровне фреймворка.

Метод runWithContext предназначен для вызова функции и передачи ей явного контекста Nuxt. Обычно контекст Nuxt передаётся неявно, и вам не нужно беспокоиться об этом. Однако в сложных сценариях с async/await в middleware или плагинах контекст может сброситься после асинхронного шага.

middleware/auth.ts
export default defineNuxtRouteMiddleware(async (to, from) => {
  const nuxtApp = useNuxtApp()
  let user
  try {
    user = await fetchUser()
    // компилятор Vue/Nuxt теряет контекст из-за try/catch с await
  } catch (e) {
    user = null
  }
  if (!user) {
    // а теперь применяем к нашему вызову `navigateTo` правильный Nuxt-контекст.
    return nuxtApp.runWithContext(() => navigateTo('/auth'))
  }
})

Использование

const result = nuxtApp.runWithContext(() => functionWithContext())
  • functionWithContext — любая функция, которой нужен текущий контекст Nuxt; контекст подставится автоматически.

runWithContext вернёт то, что вернёт functionWithContext.

Более глубокое объяснение контекста

Composition API Vue (и композаблы Nuxt) опираются на неявный контекст: в ходе жизненного цикла Vue кладёт текущий компонент (а Nuxt — nuxtApp) в глобальную переменную и снимает её в том же тике. На сервере параллельно идут запросы разных пользователей при одном процессе, поэтому Nuxt и Vue сразу снимают глобальный экземпляр, чтобы не смешивать контексты.

Итог: композаблы доступны только в том же тике, до первой асинхронной операции:

// --- Внутренняя часть Vue ---
const _vueInstance = null
const getCurrentInstance = () => _vueInstance
// ---

// Vue / Nuxt устанавливает глобальную переменную, ссылающуюся на текущий компонент, в _vueInstance при вызове setup()
async function setup () {
  getCurrentInstance() // Работает
  await someAsyncOperation() // Vue отменяет контекст в том же тике перед async-операцией!
  getCurrentInstance() // null
}

Классический обход — сохранить const instance = getCurrentInstance() и передавать экземпляр явно во вложенные вызовы, но тогда композаблы перестают опираться на неявный контекст — это компромисс дизайна, а не «баг» Vue.

Чтобы преодолеть это ограничение, Vue выполняет некоторую закулисную работу при компиляции кода нашего приложения и восстанавливает контекст после каждого вызова <script setup>:

const __instance = getCurrentInstance() // Генерируется компилятором Vue
getCurrentInstance() // Работает!
await someAsyncOperation() // Vue "снимает" контекст
__restoreInstance(__instance) // Генерируется компилятором Vue
getCurrentInstance() // снова работает

Для лучшего описания того, что на самом деле делает Vue, смотрите unjs/unctx#2 (комментарий).

Решение

Именно здесь можно использовать runWithContext для восстановления контекста, аналогично тому, как работает <script setup>.

Внутри Nuxt использует unjs/unctx, чтобы плагины и прослойки маршрутов (middleware) вели себя как композаблы Vue: navigateTo() и аналоги работают без явной передачи nuxtApp.

У Nuxt-композаблов та же модель, что у Composition API, поэтому нужна такая же поддержка контекста. См. unjs/unctx#2, unjs/unctx#4 и nuxt/framework#3884.

Vue пока поддерживает восстановление async-контекста для <script setup> при использовании async/await. В Nuxt добавлена трансформация для defineNuxtPlugin() и defineNuxtRouteMiddleware(): при их использовании Nuxt автоматически применяет восстановление контекста.

Остающиеся проблемы

Автовосстановление контекста в unctx пока некорректно ведёт себя с try/catch, внутри которых есть await; со временем это должно убрать необходимость в обходных путях вроде runWithContext.

Нативный async-контекст

Используя новую экспериментальную возможность, можно включить нативную поддержку асинхронного контекста, используя AsyncLocalStorage Node.js и новую поддержку unctx, чтобы сделать асинхронный контекст доступным нативно для любого вложенного асинхронного композабла без необходимости преобразования или ручной передачи/вызова контекста.

Нативная поддержка async-контекста в настоящее время работает в Bun и Node.
Узнать больше Docs > 3 X > Guide > Going Further > Experimental Features#asynccontext.

tryUseNuxtApp

Эта функция работает точно так же, как и useNuxtApp, но вместо исключения возвращает null, если контекст недоступен.

Вы можете использовать её для композаблов, которые не требуют nuxtApp, или для простой проверки наличия или отсутствия контекста без исключения.

Пример использования:

composable.ts
export function useStandType () {
  // Всегда работает на клиенте
  if (tryUseNuxtApp()) {
    return useRuntimeConfig().public.STAND_TYPE
  } else {
    return process.env.STAND_TYPE
  }
}