useNuxtApp
useNuxtApp
- это встроенный композабл, предоставляющий доступ к общему контексту времени выполнения Nuxt, также известному как Nuxt-контекст, который доступен как на клиенте, так и на сервере. Он помогает вам получить доступ к экземпляру приложения Vue, runtime-хукам, переменным runtime-конфига и внутренним состояниям, таким как ssrContext
и payload
.
<script setup lang="ts">
const nuxtApp = useNuxtApp()
</script>
Если контекст времени выполнения недоступен в вашей области видимости, useNuxtApp
выбросит исключение при вызове. Вместо этого вы можете использовать tryUseNuxtApp
для композаблов, которые не требуют nuxtApp
, или просто для проверки доступности контекста без исключения.
Методы
provide (name, value)
nuxtApp
- это контекст времени выполнения, который вы можете расширить с помощью Nuxt-плагинов. Используйте функцию provide
для создания плагинов Nuxt, чтобы сделать значения и вспомогательные методы доступными для всех композаблов и компонентов вашего приложения Nuxt.
Функция provide
принимает параметры name
и value
.
const nuxtApp = useNuxtApp()
nuxtApp.provide('hello', (name) => `Привет, ${name}!`)
// Выведет "Привет, name!"
console.log(nuxtApp.$hello('name'))
Как видно из примера выше, $hello
стал новой пользовательской частью контекста nuxtApp
и доступен везде, где доступен nuxtApp
.
hook(name, cb)
Хуки, доступные в nuxtApp
, позволяют вам настраивать аспекты времени выполнения вашего Nuxt-приложения. Вы можете использовать runtime-хуки в композаблах Vue.js и Nuxt-плагинах, чтобы подключиться к жизненному циклу рендеринга.
Функция hook
полезна для добавления пользовательской логики, путем подключения к жизненному циклу рендеринга в определенный момент. Функция hook
в основном используется при создании плагинов Nuxt.
Смотрите хуки жизненного цикла для получения информации о доступных runtime-хуках, вызываемых Nuxt.
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.js-плагин (пример).
ssrContext
ssrContext
генерируется во время рендеринга на сервере и доступен только на сервере.
Nuxt предоставляет следующие свойства через ssrContext
:
url
(string) - Текущий url запроса.event
(request event - unjs/h3) - Доступ к запросу и ответу текущего маршрута.payload
(object) - Объект полезной нагрузки NuxtApp.
payload
payload
передает данные и переменные состояния с сервера на клиент. Следующие ключи будут доступны клиенту после того, как они будут переданы с сервера:
ServerRendered
(boolean) - Указывает, что ответ уже отрендерен на сервере.data
(object) - Когда вы получаете данные из конечной точки API, используя либоuseFetch
, либоuseAsyncData
, результирующая полезная нагрузка может быть доступна изpayload.data
. Эти данные кэшируются и помогают предотвратить получение одних и тех же данных в случае, если идентичный запрос выполняется несколько раз.<script setup lang="ts"> const { data } = await useAsyncData('count', () => $fetch('/api/count')) </script>
После получения значенияcount
с помощьюuseAsyncData
в примере выше, если вы обратитесь кpayload.data
, вы увидите{ count: 1 }
, записанные в нем.
При обращении к тем же даннымpayload.data
изssrcontext
, вы можете получить доступ к тому же значению и на стороне сервера.state
(object) - Когда вы используете композаблuseState
в Nuxt для установки общего состояния, доступ к этим данным состояния осуществляется через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 на клиенте.
export default defineComponent({
setup (_props, { slots, emit }) {
const nuxtApp = useNuxtApp()
onErrorCaptured((err) => {
if (import.meta.client && !nuxtApp.isHydrating) {
// ...
}
})
}
})
runWithContext
Метод runWithContext
предназначен для вызова функции и передачи ей явного контекста Nuxt. Обычно контекст Nuxt передается неявно, и вам не нужно беспокоиться об этом. Однако при работе со сложными сценариями async
/await
в middleware/плагинах вы можете столкнуться с ситуацией, когда текущий экземпляр был сброшен после вызова async.
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) {
// а теперь применяем к нашему вызову `navigateTo` правильный Nuxt-контекст.
return nuxtApp.runWithContext(() => navigateTo('/auth'))
}
})
Использование
const result = nuxtApp.runWithContext(() => functionWithContext())
functionWithContext
: Любая функция, которая требует контекст текущего приложения Nuxt. Этот контекст будет правильно применен автоматически.
runWithContext
вернет то, что возвращает functionWithContext
.
Более глубокое объяснение контекста
Composition API Vue.js (и композаблы Nuxt аналогично) работают в зависимости от неявного контекста. Во время жизненного цикла Vue устанавливает временный экземпляр текущего компонента (а Nuxt - временный экземпляр nuxtApp) в глобальную переменную и сбрасывает (unset) его в том же тике. При рендеринге на сервере происходит множество запросов от разных пользователей, а nuxtApp работает в одном глобальном контексте. Поэтому Nuxt и Vue немедленно отменяют установку этого глобального инстанса, чтобы избежать утечки общей ссылки между двумя пользователями или компонентами.
Что это значит? Composition API и Nuxt-композаблы доступны только во время жизненного цикла и в том же тике перед любой асинхронной операцией:
// --- Внутренняя часть Vue ---
const _vueInstance = null
const getCurrentInstance = () => _vueInstance
// ---
// Vue / Nuxt устанавливает глобальную переменную, ссылающуюся на текущий компонент, в _vueInstance при вызове setup()
async function setup() {
getCurrentInstance() // Работает
await someAsyncOperation() // Vue отменяет контекст в том же тике перед async-операцией!
getCurrentInstance() // null
}
Классическим решением этого является кэширование текущего экземпляра при первом вызове в локальную переменную типа const instance = getCurrentInstance()
и использование ее в следующем вызове композабла, но проблема в том, что тогда любой вложенный вызов композабла должен явно принимать экземпляр в качестве аргумента, а не зависеть от неявного контекста Composition API. Это ограничение дизайна композаблов, а не проблема как таковая.
Чтобы преодолеть это ограничение, 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 для поддержки композаблов, подобных Vue, для плагинов и middleware. Это позволяет композаблам, таким как navigateTo()
, работать без непосредственной передачи им nuxtApp
- привнося DX и преимущества производительности Composition API во весь фреймворк Nuxt.
Nuxt-композаблы имеют тот же дизайн, что и Vue Composition API, и поэтому нуждаются в аналогичном решении, чтобы волшебным образом выполнять это преобразование. Посмотрите unjs/unctx#2 (предложение), unjs/unctx#4 (реализация трансформации) и nuxt/framework#3884 (интеграция в Nuxt).
В настоящее время Vue поддерживает только асинхронное восстановление контекста для <script setup>
при использовании async/await. В Nuxt 3 была добавлена поддержка преобразования для defineNuxtPlugin()
и defineNuxtRouteMiddleware()
, что означает, что при их использовании Nuxt автоматически преобразует их с восстановлением контекста.
Остающиеся проблемы
Преобразование unjs/unctx
для автоматического восстановления контекста, похоже, имеет ошибку с операторами try/catch
, содержащими await
, которая в конечном итоге должна быть решена, чтобы устранить необходимость в предложенном выше обходном пути.
Нативный async-контекст
Используя новую экспериментальную возможность, можно включить нативную поддержку асинхронного контекста, используя AsyncLocalStorage
Node.js и новую поддержку unctx, чтобы сделать асинхронный контекст доступным нативно для любого вложенного асинхронного композабла без необходимости преобразования или ручной передачи/вызова контекста.
tryUseNuxtApp
Эта функция работает точно так же, как и useNuxtApp
, но вместо исключения возвращает null
, если контекст недоступен.
Вы можете использовать ее для композаблов, которые не требуют nuxtApp
, или для простой проверки наличия или отсутствия контекста без исключения.
Пример использования:
export function useStandType() {
// Всегда работает на клиенте
if (tryUseNuxtApp()) {
return useRuntimeConfig().public.STAND_TYPE
} else {
return process.env.STAND_TYPE
}
}