middleware (прослойки)

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

В Nuxt есть настраиваемый маршрутный мидлвар для выполнения кода перед переходом на маршрут.

Типы маршрутного мидлвара:

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

Первый и второй типы задаются в definePageMeta.

Имена мидлваров приводятся к kebab-case: myMiddlewaremy-middleware.
Маршрутный мидлвар выполняется во Vue-части приложения. Он не связан с серверным мидлваром, который работает в Nitro.

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

Маршрутный мидлвар — это навигационный guard, он получает текущий и следующий маршрут.

middleware/my-middleware.ts
export default defineNuxtRouteMiddleware((to, from) => {
  if (to.params.id === '1') {
    return abortNavigation()
  }
  if (to.path !== '/') {
    return navigateTo('/')
  }
})

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

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

В отличие от navigation guards vue-router, третьего аргумента next() нет — редирект и отмена задаются возвращаемым значением.

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

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

Порядок мидлваров

  1. Глобальный мидлвар
  2. Мидлвары страницы (при массиве — в указанном порядке)

Пример:

app/middleware/ directory
-| middleware/
---| analytics.global.ts
---| setup.global.ts
---| auth.ts
pages/profile.vue
<script setup lang="ts">
definePageMeta({
  middleware: [
    function (to, from) {
      // Инлайновый мидлвар
    },
    'auth',
  ],
})
</script>

Порядок выполнения:

  1. analytics.global.ts
  2. setup.global.ts
  3. Инлайновый мидлвар
  4. auth.ts

Порядок глобальных мидлваров

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

Чтобы задать порядок (например, setup.global.ts перед analytics.global.ts), используйте числовой префикс:

Directory structure
-| middleware/
---| 01.setup.global.ts
---| 02.analytics.global.ts
---| auth.ts
Имена файлов сортируются как строки: 10.new.global.ts будет перед 2.new.global.ts. Поэтому в примере используется префикс 0 для однозначных номеров.

Когда выполняется мидлвар

При SSR или статической генерации мидлвар первой страницы выполняется и при рендере, и затем на клиенте. Это нужно, если мидлвар зависит от браузера (localStorage, кэш и т.д.).

Чтобы избежать двойного запуска:

middleware/example.ts
export default defineNuxtRouteMiddleware((to) => {
  if (import.meta.server) {
    return
  }
  if (import.meta.client) {
    return
  }
  const nuxtApp = useNuxtApp()
  if (import.meta.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) {
    return
  }
})

Даже если на сервере мидлвар выбросит ошибку и отрисуется страница ошибки, на клиенте мидлвар всё равно выполнится снова.

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

Доступ к маршруту в мидлваре

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

Вызов композабла, который внутри использует useRoute(), тоже может вызвать это предупреждение и ту же проблему. Лучше передавать маршрут в функцию аргументом.
// @errors: 2304
export default defineNuxtRouteMiddleware((to) => {
  doSomethingWithRoute(to)
  callsRouteInternally()
})

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

Глобальный или именованный мидлвар можно добавить вручную через addRouteMiddleware(), например из плагина:

export default defineNuxtPlugin(() => {
  addRouteMiddleware('global-test', () => {
    console.log('this global middleware was added in a plugin and will be run on every route change')
  }, { global: true })

  addRouteMiddleware('named-test', () => {
    console.log('this named middleware was added in a plugin and would override any existing middleware of the same name')
  })
})

Пример

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

На странице:

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

Перед переходом на эту страницу будет выполнен мидлвар auth.

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

Задание мидлвара при сборке

Вместо definePageMeta на каждой странице можно задать именованный мидлвар в хуке 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 (/* some condition */ Math.random() > 0.5) {
            page.meta ||= {}
            page.meta.middleware = ['named']
          }
          if (page.children) {
            setMiddleware(page.children)
          }
        }
      }
      setMiddleware(pages)
    },
  },
})