pages (страницы)

Nuxt использует файловую маршрутизацию для создания маршрутов в веб-приложении.
Чтобы уменьшить размер бандла приложения, эта директория необязательна: vue-router не подключается, если используется только app.vue. Чтобы принудительно включить систему страниц, задайте pages: true в nuxt.config или добавьте router.options.ts.

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

Страницы — это Vue-компоненты с любым допустимым расширением, которое поддерживает Nuxt (по умолчанию .vue, .js, .jsx, .mjs, .ts или .tsx).

Nuxt автоматически создаёт маршрут для каждой страницы в каталоге ~/pages/.

<template>
  <h1>Index page</h1>
</template>

Файл app/pages/index.vue сопоставляется с маршрутом / вашего приложения.

Если вы используете app.vue, для отображения текущей страницы нужен компонент <NuxtPage/>:

app/app.vue
<template>
  <div>
    <!-- Markup shared across all pages, ex: NavBar -->
    <NuxtPage />
  </div>
</template>

У страницы должен быть один корневой элемент, чтобы работали переходы между маршрутами. HTML-комментарии тоже считаются элементами.

При серверном рендере или статической генерации содержимое маршрута отображается корректно, но при клиентской навигации переход между маршрутами может сломаться, и страница не отрендерится.

Ниже примеры того, как выглядит страница с одним корневым элементом:

<template>
  <div>
    <!-- This page correctly has only one single root element -->
    Page content
  </div>
</template>

Динамические маршруты

Если часть имени файла или каталога заключена в квадратные скобки, она становится динамическим параметром маршрута. Можно комбинировать несколько параметров и обычный текст в имени файла или папки.

Чтобы параметр был опциональным, используйте двойные квадратные скобки: например, ~/pages/[[slug]]/index.vue или ~/pages/[[slug]].vue совпадут и с /, и с /test.

Directory Structure
-| pages/
---| index.vue
---| users-[group]/
-----| [id].vue

В примере выше к group и id обращаются через объект $route:

app/pages/users-[group]/[id].vue
<template>
  <p>{{ $route.params.group }} - {{ $route.params.id }}</p>
</template>

Переход на /users-admins/123 отрендерит:

<p>admins - 123</p>

В Composition API для доступа к маршруту есть глобальная функция useRoute — она заменяет this.$route из Options API.

<script setup lang="ts">
const route = useRoute()

if (route.params.group === 'admins' && !route.params.id) {
  console.log('Warning! Make sure user is authenticated!')
}
</script>
Именованные родительские маршруты имеют приоритет над вложенными динамическими. Для /foo/hello выиграет ~/pages/foo.vue, а не ~/pages/foo/[slug].vue.
Используйте ~/pages/foo/index.vue и ~/pages/foo/[slug].vue, чтобы разные страницы обслуживали /foo и /foo/hello.

Catch-all маршрут

Для catch-all маршрута создайте файл вида [...slug].vue — он совпадёт со всеми вложенными путями под этим сегментом.

app/pages/[...slug].vue
<template>
  <p>{{ $route.params.slug }}</p>
</template>

Переход на /hello/world отрендерит:

<p>["hello", "world"]</p>

Вложенные маршруты

Вложенные маршруты отображаются через <NuxtPage>.

Пример:

Directory Structure
-| pages/
---| parent/
-----| child.vue
---| parent.vue

Такое дерево файлов даёт такие маршруты:

[
  {
    path: '/parent',
    component: '~/pages/parent.vue',
    name: 'parent',
    children: [
      {
        path: 'child',
        component: '~/pages/parent/child.vue',
        name: 'parent-child',
      },
    ],
  },
]

Чтобы отобразить компонент child.vue, вставьте <NuxtPage> в app/pages/parent.vue:

pages/parent.vue
<template>
  <div>
    <h1>I am the parent view</h1>
    <NuxtPage :foobar="123" />
  </div>
</template>
pages/parent/child.vue
<script setup lang="ts">
const props = defineProps({
  foobar: String,
})

console.log(props.foobar)
</script>

Ключ дочернего маршрута

Чтобы точнее управлять перерисовкой <NuxtPage> (например, для переходов), передайте строку или функцию в проп pageKey либо задайте key через definePageMeta:

pages/parent.vue
<template>
  <div>
    <h1>I am the parent view</h1>
    <NuxtPage :page-key="route => route.fullPath" />
  </div>
</template>

Или так:

pages/parent/child.vue
<script setup lang="ts">
definePageMeta({
  key: route => route.fullPath,
})
</script>
Прочитайте и отредактируйте живой пример в Docs > 4 X > Examples > Routing > Pages.

Группы маршрутов

Иногда нужно сгруппировать маршруты так, чтобы это не влияло на файловую маршрутизацию. Для этого файлы кладут в папку с именем в круглых скобках — ( и ).

Например:

Directory structure
-| pages/
---| index.vue
---| (marketing)/
-----| about.vue
-----| contact.vue

В приложении появятся страницы /, /about и /contact. Группа marketing для URL не используется.

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

Группы маршрутов автоматически попадают в метаданные маршрута как route.meta.groups. Это позволяет использовать информацию о группе в компонентах для условной логики, стилей и других задач.

pages/(marketing)/about.vue
<script setup lang="ts">
const route = useRoute()

console.log(route.meta.groups) // Output: ['marketing']
</script>

<template>
  <div>
    <p v-if="route.meta.groups?.includes('marketing')">
      This is a marketing page
    </p>
  </div>
</template>

Метаданные страницы

Для каждого маршрута можно задать метаданные макросом definePageMeta — он работает и в <script>, и в <script setup>:

<script setup lang="ts">
definePageMeta({
  title: 'My home page',
})
</script>

Данные доступны в приложении через объект route.meta.

<script setup lang="ts">
const route = useRoute()

console.log(route.meta.title) // My home page
</script>

При вложенных маршрутах метаданные всех совпавших маршрутов объединяются в один объект. Подробнее о meta маршрута — в документации vue-router.

Как и defineEmits или defineProps (см. документацию Vue), definePageMeta — это компиляционный макрос. Он вырезается при компиляции, поэтому на него нельзя ссылаться из кода компонента. Переданные в него метаданные поднимаются из компонента. Поэтому объект мета не может ссылаться на сам компонент. Но можно ссылаться на импорты и на локально объявленные чистые функции.

Не ссылайтесь на реактивные данные и на функции с побочными эффектами — это может привести к неожиданному поведению.
<script setup lang="ts">
import { someData } from '~/utils/example'

function validateIdParam (route) {
  return route.params.id && !Number.isNaN(Number(route.params.id))
}

const title = ref('')

definePageMeta({
  validate: validateIdParam,
  someData,
  title, // do not do this, the ref will be hoisted out of the component
})
</script>

Особые метаданные

Конечно, вы можете задавать метаданные для своих нужд. Но часть полей definePageMeta имеет особое назначение:

alias

Алиасы страницы: один и тот же маршрут доступен по разным путям. Значение — строка или массив строк, как в документации vue-router.

keepalive

При keepalive: true в definePageMeta Nuxt оборачивает страницу в компонент Vue <KeepAlive>. Это полезно, например, у родительского маршрута с динамическими дочерними, если нужно сохранять состояние страницы при смене маршрута.

Чтобы сохранять состояние родительских маршрутов, используйте синтаксис <NuxtPage keepalive />. Также можно передать пропы в <KeepAlive> (полный список).

Значение по умолчанию для этого свойства задаётся в nuxt.config.

key

См. выше.

layout

Лейаут для рендера маршрута: false (отключить лейаут), строка или ref/computed для реактивности. Подробнее о лейаутах.

layoutTransition и pageTransition

Параметры для компонента <transition>, оборачивающего страницы и лейауты, либо false, чтобы отключить обёртку <transition> для этого маршрута. Список опций и как работают переходы.

Значения по умолчанию задаются в nuxt.config.

middleware

Middleware выполняется перед загрузкой страницы и объединяется со всем middleware родительских/дочерних маршрутов. Можно указать строку, функцию (анонимный middleware по паттерну глобального before guard) или массив строк/функций. Подробнее об именованном middleware.

name

Имя маршрута этой страницы.

path

Сопоставитель пути, если шаблон сложнее, чем можно выразить именем файла. См. документацию vue-router.

props

Доступ к params маршрута как к пропам страницы. См. документацию vue-router.

Типизация своих метаданных

Если вы добавляете свои поля в метаданные страниц, имеет смысл типизировать объект definePageMeta:

index.d.ts
declare module '#app' {
  interface PageMeta {
    pageType?: string
  }
}

// It is always important to ensure you import/export something when augmenting a type
export {}

Навигация

Для переходов между страницами используйте компонент <NuxtLink>.

Он входит в Nuxt, отдельно импортировать его не нужно, в отличие от других компонентов.

Простая ссылка на index.vue в app/pages:

<template>
  <NuxtLink to="/">Home page</NuxtLink>
</template>
Подробнее об использовании <NuxtLink>.

Программная навигация

Программные переходы в Nuxt выполняются утилитой navigateTo(). С её помощью можно направлять пользователя по приложению — например, по отправке формы поиска вызывается метод navigate().

Всегда используйте await с navigateTo или возвращайте его результат из функций.
<script setup lang="ts">
const name = ref('')
const type = ref(1)

function navigate () {
  return navigateTo({
    path: '/search',
    query: {
      name: name.value,
      type: type.value,
    },
  })
}
</script>

Страницы только для клиента

Страницу можно пометить как только для клиента, добавив суффикс .client.vue. Содержимое такой страницы не рендерится на сервере.

Страницы только для сервера

Страницу можно пометить как только для сервера, добавив суффикс .server.vue. К ней по-прежнему можно перейти с клиента через vue-router, но рендер пойдёт через серверный компонент — код рендера страницы не попадёт в клиентский бандл.

У страниц только для сервера должен быть один корневой элемент (HTML-комментарии тоже считаются элементами).

Кастомная маршрутизация

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

Узнать больше Docs > 4 X > Guide > Recipes > Custom Routing.

Несколько каталогов pages

По умолчанию все страницы лежат в одном каталоге app/pages в корне проекта.

С помощью Nuxt Layers можно объединять страницы из разных источников:

Directory Structure
-| some-app/
---| nuxt.config.ts
---| pages/
-----| app-page.vue
-| nuxt.config.ts
some-app/nuxt.config.ts
// some-app/nuxt.config.ts
export default defineNuxtConfig({
})
nuxt.config.ts
export default defineNuxtConfig({
  extends: ['./some-app'],
})
Узнать больше Docs > 4 X > Guide > Going Further > Layers.