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

Квадратные скобки в имени файла превращают часть пути в параметр: один файл [id].vue обслуживает тысячи товаров.
Суть: динамический сегмент маршрута объявляется через квадратные скобки в имени файла, например pages/products/[id].vue. Значение параметра читается через useRoute().params. Так один шаблон обслуживает любой идентификатор в URL.

Статичные маршруты хороши, пока страниц немного. Но что делать с каталогом из тысяч товаров? Создавать файл на каждый — безумие. Здесь на сцену выходят динамические маршруты: один файл-шаблон обслуживает любое значение в части пути.

Синтаксис — квадратные скобки в имени файла. pages/products/[id].vue матчит /products/1, /products/42, /products/любая-строка. Часть пути на месте [id] становится параметром с именем id. Внутри компонента вы читаете его через useRoute():

<script setup>
const route = useRoute()
const id = route.params.id   // "42" для /products/42
</script>

<template>
  <h1>Товар №{{ id }}</h1>
</template>

Параметров может быть несколько: pages/users/[userId]/posts/[postId].vue матчит /users/7/posts/100 и даёт params.userId и params.postId. Есть и «всеохватный» (catch-all) сегмент через троеточие: pages/docs/[...slug].vue поймает /docs/a, /docs/a/b, /docs/a/b/c — удобно для документации с произвольной вложенностью.

   Шаблон файла                 Совпадает с URL

   [id].vue            ->  /products/42        (id = "42")
   [userId]/[postId]   ->  /users/7/posts/100  (два параметра)
   [...slug].vue       ->  /docs/a/b/c         (slug = ["a","b","c"])

Как работает под капотом

Из имени файла Nuxt строит шаблон маршрута для Vue Router. [id] превращается в динамический сегмент :id. При навигации роутер сопоставляет URL с зарегистрированными шаблонами, извлекает значения параметров и складывает их в route.params. Catch-all-сегмент собирает «хвост» пути в массив.

Смоделируем сам матчер — функцию, которая по шаблону и URL извлекает параметры. Это упрощённое сердце любого роутера:

// Мини-матчер: шаблон с :param против реального пути.
function matchRoute(pattern, url) {
  const pSeg = pattern.split("/").filter(Boolean);
  const uSeg = url.split("/").filter(Boolean);
  if (pSeg.length !== uSeg.length) return null;
  const params = {};
  for (let i = 0; i < pSeg.length; i++) {
    if (pSeg[i].startsWith(":")) {
      params[pSeg[i].slice(1)] = uSeg[i];   // динамический сегмент
    } else if (pSeg[i] !== uSeg[i]) {
      return null;                           // статический не совпал
    }
  }
  return params;
}

console.log(matchRoute("/products/:id", "/products/42"));
console.log(matchRoute("/users/:userId/posts/:postId", "/users/7/posts/100"));
console.log(matchRoute("/products/:id", "/about"));  // null — длины разные

Попробуй сам ▶ — именно так роутер вытаскивает params.id из URL. Nuxt лишь генерирует эти шаблоны из имён файлов.

Частые ошибки

  • Считать параметр числом. route.params.id — всегда строка. Для арифметики приводите через Number(...).
  • Не обрабатывать несуществующий id. Если товара нет, верните 404 через createError, а не показывайте пустую страницу.
  • Путать params и query. /products/42 — это params; /products?sort=price — это route.query.

Best practices

  • Один динамический шаблон вместо тысяч статичных файлов — стандарт для каталогов и блогов.
  • Для произвольной вложенности (документация, файловые пути) используйте catch-all [...slug].
  • Всегда валидируйте параметр и корректно отдавайте 404 для несуществующих сущностей.

Итог: динамические маршруты позволяют одному файлу обслуживать бесконечно много URL. Скобки задают параметр, useRoute().params его читает. Дальше посмотрим, как оборачивать страницы в общие макеты — layouts.

Динамические маршруты тесно связаны с темой получения данных, к которой мы придём позже. Типичный сценарий: на странице [id].vue вы читаете параметр, подставляете его в запрос к API и грузите данные именно этой сущности. При смене параметра (переход с товара 42 на товар 43) Nuxt умеет переиспользовать компонент и перезапросить данные, а не пересоздавать страницу с нуля. Понимание этой связки — параметр маршрута плюс реактивная загрузка по нему — открывает большинство реальных страниц-деталей: карточка товара, профиль пользователя, статья блога.

Проверьте себя
1. Какой файл нужен, чтобы обслуживать URL вида /products/42, /products/100 и т.д. одним шаблоном?
Apages/products.vue
Bpages/products/[id].vue
Cpages/products/all.vue
Dpages/[products].vue
2. Какого типа значение в route.params.id для маршрута /products/42?
AЧисло 42
BСтрока \"42\"
CОбъект
DБулево значение