middleware

Nuxt предоставляет middleware для запуска кода перед переходом по заданному маршруту.

Nuxt даёт настраиваемую middleware маршрута (в терминах Vue Router — хуки навигации): код, который выполняется перед переходом на маршрут.

Существует три вида middleware для маршрутов:

  1. Анонимная (или встроенная) middleware маршрута определяется непосредственно на странице.
  2. Именованная middleware маршрута, лежащая в middleware/ и подгружаемая асинхронным импортом при использовании на странице.
  3. Глобальная middleware маршрута в middleware/ с суффиксом .global, выполняемая при каждом изменении маршрута.

Первые два вида middleware можно определить в definePageMeta.

Имена middleware нормализуются до kebab-case: myMiddleware становится my-middleware.
Middleware маршрута запускается в части Vue-приложения Nuxt. Несмотря на похожее название, они полностью отличаются от серверных middleware, которые запускаются в части сервера приложения Nitro.

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

Middleware маршрута — это хуки навигации: им передаются текущий и целевой маршрут.

middleware/my-middleware.ts
export default 
defineNuxtRouteMiddleware
((
to
,
from
) => {
if (
to
.
params
.
id
=== '1') {
return
abortNavigation
()
} // В реальном приложении вы, вероятно, не будете перенаправлять каждый маршрут на `/`, // однако важно проверить `to.path` перед перенаправлением, иначе вы // можете получить бесконечный цикл редиректа if (
to
.
path
!== '/') {
return
navigateTo
('/')
} })

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

  1. navigateTo — перенаправление на указанный маршрут.
  2. abortNavigation — отмена навигации, опционально с ошибкой.

В отличие от навигационных гвардов в vue-router, третий аргумент next() не передаётся, а редирект и отмена навигации задаются возвращаемым значением.

Возможные возвращаемые значения:

  • ничего (простой return или отсутствие возврата вообще) - не блокирует навигацию и переходит к следующей функции middleware, если таковая имеется, или завершает навигацию по маршруту
  • return navigateTo('/') — перенаправляет по указанному пути; на сервере по умолчанию используется код 302 Found
  • return navigateTo('/', { redirectCode: 301 }) — перенаправление с кодом 301 Moved Permanently
  • return abortNavigation() - останавливает текущую навигацию
  • return abortNavigation(error) - отклоняет текущую навигацию с ошибкой
Узнать больше Docs > 3 X > API > Utils > Navigate To.
Узнать больше Docs > 3 X > API > Utils > Abort Navigation.
Для редиректов и остановки навигации лучше использовать перечисленные выше хелперы. Иные варианты возврата из документации vue-router могут сработать, но без гарантий совместимости в будущем.

Порядок middleware

Middleware работают в следующем порядке:

  1. Глобальные middleware
  2. Порядок middleware, определяемый страницей (если несколько middleware объявлены массивом)

Например, предположим, что у вас есть следующие middleware и компонент:

middleware/ directory
-| middleware/
---| analytics.global.ts
---| setup.global.ts
---| auth.ts
pages/profile.vue
<script setup lang="ts">
definePageMeta
({
middleware
: [
function (
to
,
from
) {
// Пользовательская middleware }, 'auth', ], }) </script>

Можно ожидать, что middleware будут запущены в следующем порядке:

  1. analytics.global.ts
  2. setup.global.ts
  3. Пользовательская встроенная middleware
  4. auth.ts

Порядок глобальных middleware

По умолчанию глобальные middleware выполняются в алфавитном порядке на основе имени файла.

Однако иногда нужен явный порядок. Например, setup.global.ts должен выполняться раньше analytics.global.ts. Тогда задайте глобальным middleware префикс с «алфавитной» нумерацией.

Directory structure
-| middleware/
---| 01.setup.global.ts
---| 02.analytics.global.ts
---| auth.ts
Если вы новичок в «алфавитной» нумерации, помните, что имена файлов сортируются как строки, а не как числовые значения. Например, 10.new.global.ts будет предшествовать 2.new.global.ts. Вот почему в примере номера из одной цифры имеют префикс 0.

Когда запускаются middleware

Если сайт рендерится или генерируется сервером, middleware для начальной страницы будет выполняться как при рендеринге страницы, так и снова на клиенте. Это может быть необходимо, если вашей middleware требуется окружение браузера, например, если у вас есть сгенерированный сайт, агрессивно кэширующий ответы или вы хотите прочитать значение из локального хранилища.

Однако, если вы хотите избежать такого поведения, вы можете сделать следующее:

middleware/example.ts
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 всё равно выполнится снова в браузере.

Отрисовка страницы ошибки — это совершенно отдельная загрузка страницы, что означает, что любая зарегистрированная middleware будет запущена снова. Вы можете использовать useError в middleware, чтобы проверить, обрабатывается ли ошибка.

Доступ к маршруту в middleware

Всегда используйте параметры to и from в middleware для доступа к следующему и предыдущему маршруту. Избегайте композабла useRoute() в этом контексте. В middleware нет понятия «текущего маршрута»: навигация может быть отменена или перенаправлена. В таком контексте useRoute() будет давать неверные данные.

Иногда вы вызываете композабл, который внутри использует useRoute(), из‑за чего предупреждение появляется даже без прямого вызова в middleware. Это приводит к той же проблеме, поэтому передавайте маршрут аргументом в функции, которые вызываются из middleware.
export default 
defineNuxtRouteMiddleware
((
to
) => {
// передаём маршрут в функцию, чтобы не вызывать `useRoute()` внутри middleware doSomethingWithRoute(
to
)
Cannot find name 'doSomethingWithRoute'.
// ❌ так появится предупреждение и это не рекомендуется callsRouteInternally()
Cannot find name 'callsRouteInternally'.
})

Динамическое добавление middleware

Глобальную или именованную middleware маршрута можно добавить вручную с помощью вспомогательной функции addRouteMiddleware(), например, из плагина.

export default 
defineNuxtPlugin
(() => {
addRouteMiddleware
('global-test', () => {
console
.
log
('эта глобальная middleware была добавлена в плагин и будет запускаться при каждом изменении маршрута')
}, {
global
: true })
addRouteMiddleware
('named-test', () => {
console
.
log
('эта именованная middleware была добавлена в плагин и переопределит любую существующую middleware с тем же именем')
}) })

Пример

Directory Structure
-| middleware/
---| auth.ts

В файле страницы вы можете сослаться на эту middleware маршрута:

<script setup lang="ts">
definePageMeta
({
middleware
: ['auth'],
// или middleware: 'auth' }) </script>

Теперь, прежде чем переход на эту страницу сможет быть завершен, будет запущена middleware маршрута auth.

Прочитайте и отредактируйте живой пример в Docs > 3 X > Examples > Routing > Middleware.

Настройка middleware во время сборки

Вместо использования definePageMeta на каждой странице, вы можете добавить именованную middleware маршрута в хуке pages:extend.

nuxt.config.ts
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)
    },
  },
})