middleware
Nuxt даёт настраиваемую middleware маршрута (в терминах Vue Router — хуки навигации): код, который выполняется перед переходом на маршрут.
Существует три вида middleware для маршрутов:
- Анонимная (или встроенная) middleware маршрута определяется непосредственно на странице.
- Именованная middleware маршрута, лежащая в
middleware/и подгружаемая асинхронным импортом при использовании на странице. - Глобальная middleware маршрута в
middleware/с суффиксом.global, выполняемая при каждом изменении маршрута.
Первые два вида middleware можно определить в definePageMeta.
myMiddleware становится my-middleware.Использование
Middleware маршрута — это хуки навигации: им передаются текущий и целевой маршрут.
export default defineNuxtRouteMiddleware((to, from) => {
if (to.params.id === '1') {
return abortNavigation()
}
// В реальном приложении вы, вероятно, не будете перенаправлять каждый маршрут на `/`,
// однако важно проверить `to.path` перед перенаправлением, иначе вы
// можете получить бесконечный цикл редиректа
if (to.path !== '/') {
return navigateTo('/')
}
})
Nuxt предоставляет два глобальных хелпера, которые можно вернуть из middleware:
navigateTo— перенаправление на указанный маршрут.abortNavigation— отмена навигации, опционально с ошибкой.
В отличие от навигационных гвардов в vue-router, третий аргумент next() не передаётся, а редирект и отмена навигации задаются возвращаемым значением.
Возможные возвращаемые значения:
- ничего (простой
returnили отсутствие возврата вообще) - не блокирует навигацию и переходит к следующей функции middleware, если таковая имеется, или завершает навигацию по маршруту return navigateTo('/')— перенаправляет по указанному пути; на сервере по умолчанию используется код302Foundreturn navigateTo('/', { redirectCode: 301 })— перенаправление с кодом301Moved Permanentlyreturn abortNavigation()- останавливает текущую навигациюreturn abortNavigation(error)- отклоняет текущую навигацию с ошибкой
Порядок middleware
Middleware работают в следующем порядке:
- Глобальные middleware
- Порядок middleware, определяемый страницей (если несколько middleware объявлены массивом)
Например, предположим, что у вас есть следующие middleware и компонент:
-| middleware/
---| analytics.global.ts
---| setup.global.ts
---| auth.ts
<script setup lang="ts">
definePageMeta({
middleware: [
function (to, from) {
// Пользовательская middleware
},
'auth',
],
})
</script>
Можно ожидать, что middleware будут запущены в следующем порядке:
analytics.global.tssetup.global.ts- Пользовательская встроенная middleware
auth.ts
Порядок глобальных middleware
По умолчанию глобальные middleware выполняются в алфавитном порядке на основе имени файла.
Однако иногда нужен явный порядок. Например, setup.global.ts должен выполняться раньше analytics.global.ts. Тогда задайте глобальным middleware префикс с «алфавитной» нумерацией.
-| middleware/
---| 01.setup.global.ts
---| 02.analytics.global.ts
---| auth.ts
10.new.global.ts будет предшествовать 2.new.global.ts. Вот почему в примере номера из одной цифры имеют префикс 0.Когда запускаются middleware
Если сайт рендерится или генерируется сервером, middleware для начальной страницы будет выполняться как при рендеринге страницы, так и снова на клиенте. Это может быть необходимо, если вашей middleware требуется окружение браузера, например, если у вас есть сгенерированный сайт, агрессивно кэширующий ответы или вы хотите прочитать значение из локального хранилища.
Однако, если вы хотите избежать такого поведения, вы можете сделать следующее:
export default defineNuxtRouteMiddleware((to) => {
// пропустить middleware на сервере
if (import.meta.server) {
return
}
// полностью пропустить middleware на стороне клиента
if (import.meta.client) {
return
}
// или пропустить middleware только при начальной загрузке клиента
const nuxtApp = useNuxtApp()
if (import.meta.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) {
return
}
})
Так происходит даже если на сервере в middleware выброшена ошибка и отрендерена страница ошибки: middleware всё равно выполнится снова в браузере.
useError в middleware, чтобы проверить, обрабатывается ли ошибка.Доступ к маршруту в middleware
Всегда используйте параметры to и from в middleware для доступа к следующему и предыдущему маршруту. Избегайте композабла useRoute() в этом контексте.
В middleware нет понятия «текущего маршрута»: навигация может быть отменена или перенаправлена. В таком контексте useRoute() будет давать неверные данные.
useRoute(), из‑за чего предупреждение появляется даже без прямого вызова в middleware.
Это приводит к той же проблеме, поэтому передавайте маршрут аргументом в функции, которые вызываются из middleware.// @errors: 2304
export default defineNuxtRouteMiddleware((to) => {
// передаём маршрут в функцию, чтобы не вызывать `useRoute()` внутри middleware
doSomethingWithRoute(to)
// ❌ так появится предупреждение и это не рекомендуется
callsRouteInternally()
})
// маршрут передаётся аргументом — корректно для использования в middleware
export function doSomethingWithRoute (route = useRoute()) {
// ...
}
// ❌ эта функция не подходит для вызова из middleware
export function callsRouteInternally () {
const route = useRoute()
// ...
}
Динамическое добавление middleware
Глобальную или именованную middleware маршрута можно добавить вручную с помощью вспомогательной функции addRouteMiddleware(), например, из плагина.
export default defineNuxtPlugin(() => {
addRouteMiddleware('global-test', () => {
console.log('эта глобальная middleware была добавлена в плагин и будет запускаться при каждом изменении маршрута')
}, { global: true })
addRouteMiddleware('named-test', () => {
console.log('эта именованная middleware была добавлена в плагин и переопределит любую существующую middleware с тем же именем')
})
})
Пример
-| middleware/
---| auth.ts
В файле страницы вы можете сослаться на эту middleware маршрута:
<script setup lang="ts">
definePageMeta({
middleware: ['auth'],
// или middleware: 'auth'
})
</script>
Теперь, прежде чем переход на эту страницу сможет быть завершен, будет запущена middleware маршрута auth.
Настройка middleware во время сборки
Вместо использования definePageMeta на каждой странице, вы можете добавить именованную middleware маршрута в хуке pages:extend.
import type { NuxtPage } from 'nuxt/schema'
export default defineNuxtConfig({
hooks: {
'pages:extend' (pages) {
function setMiddleware (pages: NuxtPage[]) {
for (const page of pages) {
if (/* некоторое условие */ Math.random() > 0.5) {
page.meta ||= {}
// Обратите внимание, что это переопределит любые middleware, заданные в `definePageMeta` на странице.
page.meta.middleware = ['named']
}
if (page.children) {
setMiddleware(page.children)
}
}
}
setMiddleware(pages)
},
},
})