Перейти к содержимому

Маршрутизация

Astro использует маршрутизацию на основе файлов для генерации URL-адресов ваших сборок на основе схемы расположения файлов в каталоге src/pages/ вашего проекта.

Astro использует стандартные HTML-элементы <a> для навигации между маршрутами. Нет специфического для фреймворка компонента <Link>.

src/pages/index.astro
<p>Read more <a href="/about/">about</a> Astro!</p>

Компоненты страниц .astro, а также Markdown и MDX файлы (.md, .mdx) в директории src/pages/ автоматически становятся страницами вашего сайта. Маршрут каждой страницы соответствует ее пути и имени файла в каталоге src/pages/.

# Пример: Статические маршруты
src/pages/index.astro -> mysite.com/
src/pages/about.astro -> mysite.com/about
src/pages/about/index.astro -> mysite.com/about
src/pages/about/me.astro -> mysite.com/about/me
src/pages/posts/1.md -> mysite.com/posts/1

В имени файла страницы Astro могут быть указаны параметры динамического маршрута для создания нескольких подходящих страниц. Например, src/pages/authors/[author].astro генерирует биографическую страницу для каждого автора в вашем блоге. author становится параметром, к которому вы можете обращаться внутри страницы.

В стандартном режиме статического вывода Astro эти страницы генерируются во время сборки, поэтому вы должны заранее определить список авторов, которые получат соответствующий файл. В режиме SSR страница будет генерироваться по запросу для любого маршрута, который соответствует.

Поскольку все маршруты должны быть определены во время сборки, динамический маршрут должен экспортировать функцию getStaticPaths(), которая возвращает массив объектов со свойством params. Каждый из этих объектов будет генерировать соответствующий маршрут.

[dog].astro определяет динамический параметр dog в своем имени, поэтому объекты, возвращаемые getStaticPaths(), должны включать dog в свои params. Затем страница может получить доступ к этому параметру с помощью Astro.params.

src/pages/dogs/[dog].astro
---
export function getStaticPaths() {
return [
{params: {dog: 'clifford'}},
{params: {dog: 'rover'}},
{params: {dog: 'spot'}},
];
}
const { dog } = Astro.params;
---
<div>Good dog, {dog}!</div>

В результате будут сгенерированы три страницы: /dogs/clifford, /dogs/rover и /dogs/spot, каждая из которых отображает соответствующее имя собаки.

Имя файла может включать несколько параметров, которые должны быть включены в объекты params в getStaticPaths():

src/pages/[lang]-[version]/info.astro
---
export function getStaticPaths () {
return [
{params: {lang: 'en', version: 'v1'}},
{params: {lang: 'fr', version: 'v2'}},
];
}
const { lang, version } = Astro.params;
---
...

В результате будут сгенерированы /en-v1/info и /fr-v2/info.

Параметры могут быть включены в отдельные части пути. Например, файл src/pages/[lang]/[version]/info.astro с тем же getStaticPaths(), что и выше, сгенерирует маршруты /en/v1/info и /fr/v2/info.

Узнайте больше о getStaticPaths().
Связанная инструкция: Add i18n features (EN)

Если вам нужна большая гибкость в маршрутизации URL, вы можете использовать rest параметры ([...path]) в имени файла .astro для сопоставления путей файлов любой глубины:

src/pages/sequences/[...path].astro
---
export function getStaticPaths() {
return [
{params: {path: 'one/two/three'}},
{params: {path: 'four'}},
{params: {path: undefined }}
]
}
const { path } = Astro.params;
---
...

Это сгенерирует /sequences/one/two/three, /sequences/four и /sequences. (Установка параметра rest в значение undefined позволяет ему соответствовать странице верхнего уровня).

Параметры rest можно использовать с другими именованными параметрами. Например, просмотрщик файлов GitHub может быть представлен следующим динамическим маршрутом:

/[org]/[repo]/tree/[branch]/[...file]

В этом примере запрос на /withastro/astro/tree/main/docs/public/favicon.svg будет разбит на следующие именованные параметры:

{
org: 'withastro',
repo: 'astro',
branch: 'main',
file: 'docs/public/favicon.svg'
}

Пример: Динамические страницы на нескольких уровнях

Заголовок раздела Пример: Динамические страницы на нескольких уровнях

В следующем примере параметр rest ([...slug]) и функция props в getStaticPaths() генерируют страницы для слагов разной глубины.

src/pages/[...slug].astro
---
export async function getStaticPaths() {
const pages = [
{
slug: undefined,
title: "Astro Store",
text: "Welcome to the Astro store!",
},
{
slug: "products",
title: "Astro products",
text: "We have lots of products for you",
},
{
slug: "products/astro-handbook",
title: "The ultimate Astro handbook",
text: "If you want to learn Astro, you must read this book.",
},
];
return pages.map(({ slug, title, text }) => {
return {
params: { slug },
props: { title, text },
};
});
}
const { title, text } = Astro.props;
---
<html>
<head>
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<p>{text}</p>
</body>
</html>

В режиме SSR динамические маршруты определяются точно так же: включите в имена файлов скобки [param] или [...path] для соответствия произвольным строкам или путям. Но поскольку маршруты больше не строятся заранее, страница будет обслуживаться по любому подходящему маршруту. Поскольку это не “статические” маршруты, getStaticPaths не следует использовать.

src/pages/resources/[resource]/[id].astro
---
const { resource, id } = Astro.params;
---
<h1>{resource}: {id}</h1>

Эта страница будет предоставлена для любого значения resource и id: resources/users/1, resources/colors/blue и т.д.

Поскольку страницы SSR не могут использовать getStaticPaths(), они не могут получать props. Предыдущий пример может быть адаптирован для режима SSR путем поиска значения параметра slug в объекте. Если маршрут находится в корне (”/”), параметр slug будет иметь значение undefined. Если значение не существует в объекте, мы перенаправляем на страницу 404.

src/pages/[...slug].astro
---
const pages = [
{
slug: undefined,
title: 'Astro Store',
text: 'Welcome to the Astro store!',
},
{
slug: 'products',
title: 'Astro products',
text: 'We have lots of products for you',
},
{
slug: 'products/astro-handbook',
title: 'The ultimate Astro handbook',
text: 'If you want to learn Astro, you must read this book.',
}
];
const { slug } = Astro.params;
const page = pages.find((page) => page.slug === slug);
if (!page) return Astro.redirect("/404");
const { title, text } = page;
---
<html>
<head>
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<p>{text}</p>
</body>
</html>

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

Вы можете определить правила для перенаправления пользователей на постоянно перемещаемые страницы в конфигурации Astro. Или перенаправлять пользователей динамически по мере использования ими вашего сайта.

Добавлено в: astro@2.9.0

Вы можете указать отображение постоянных переадресаций в конфигурации Astro с помощью значения redirects. Для большинства переадресаций это сопоставление старого маршрута с новым маршрутом:

astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
redirects: {
'/old-page': '/new-page'
}
});

Эти переадресации подчиняются тем же правилам, что и маршруты на основе файлов. Динамические маршруты разрешены, если новый и старый маршруты содержат одинаковые параметры, например:

{
"/blog/[...slug]": "/articles/[...slug]"
}

Используя SSR или статический адаптер, вы также можете предоставить объект в качестве значения, что позволит вам указать код ответа status в дополнение к новому адресу destination:

astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
redirects: {
'/old-page': {
status: 302,
destination: '/new-page'
}
}
});

При запуске astro build, Astro по умолчанию выводит HTML-файлы с тегом meta refresh. Поддерживаемые адаптеры вместо этого будут выводить конфигурационный файл хоста с переадресацией.

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

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

src/pages/account.astro
---
import { isLoggedIn } from '../utils';
const cookie = Astro.request.headers.get('cookie');
// Если пользователь не вошел в систему, перенаправляем его на страницу входа
if (!isLoggedIn(cookie)) {
return Astro.redirect('/login');
}
---
<html>
<!-- Page here... -->
</html>

Возможно, что несколько определенных маршрутов будут пытаться построить один и тот же путь URL. Например, все эти маршруты могут построить /posts/create:

  • Директорияsrc/pages/
    • […slug].astro
    • Директорияposts/
      • create.astro
      • [page].astro
      • [pid].ts
      • […slug].astro

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

  • Маршруты с большим количеством сегментов пути будут иметь приоритет перед менее специфичными маршрутами. В приведенном выше примере все маршруты в разделе /posts/ имеют приоритет над корневым /[...slug].astro.
  • Статические маршруты без параметров пути будут иметь приоритет над динамическими маршрутами. Например, /posts/create.astro имеет приоритет над всеми остальными маршрутами в примере.
  • Динамические маршруты с именованными параметрами имеют приоритет над остальными параметрами. Например, /posts/[page].astro имеет приоритет над /posts/[...slug].astro.
  • Предрендеренные динамические маршруты имеют приоритет над серверными динамическими маршрутами.
  • Эндпоинты имеют приоритет над страницами.
  • Если ни одно из вышеперечисленных правил не определяет порядок, маршруты сортируются в алфавитном порядке на основе локали по умолчанию вашей установки Node.

Учитывая приведенный выше пример, вот несколько примеров того, как правила будут сопоставлять запрашиваемый URL с маршрутом, используемым для построения HTML:

  • pages/posts/create.astro - Будет построено только /posts/create.
  • pages/posts/[pid].ts - Будет построено /posts/abc, /posts/xyz и т.д. Но не /posts/create.
  • pages/posts/[page].astro - Построит /posts/1, /posts/2 и т.д. Но не /posts/create, /posts/abc и /posts/xyz.
  • pages/posts/[...slug].astro - Будет создавать /posts/1/2, /posts/a/b/c и т.д. Но не /posts/create, /posts/1, /posts/abc и т.д.
  • pages/[...slug].astro - Будет создавать /abc, /xyz, /abc/xyz и т.д. Но не /posts/create, /posts/1, /posts/abc, и т.д.

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

Имена пагинационных маршрутов должны использовать тот же синтаксис [скобка], что и стандартные динамические маршруты. Например, имя файла /astronauts/[page].astro будет генерировать маршруты для /astronauts/1, /astronauts/2 и т. д., где [page] - это номер генерируемой страницы.

Вы можете использовать функцию paginate() для генерации этих страниц для массива значений следующим образом:

src/pages/astronauts/[page].astro
---
export async function getStaticPaths({ paginate }) {
const astronautPages = [{
astronaut: 'Neil Armstrong',
}, {
astronaut: 'Buzz Aldrin',
}, {
astronaut: 'Sally Ride',
}, {
astronaut: 'John Glenn',
}];
// Генерируем страницы из нашего массива астронавтов, по 2 на страницу
return paginate(astronautPages, { pageSize: 2 });
}
// Все пагинированные данные передаются по пропсу "page"
const { page } = Astro.props;
---
<!--Отображает номер текущей страницы. Также можно использовать Astro.params.page!-->
<h1>Page {page.currentPage}</h1>
<ul>
<!--Перечисление массива информации об астронавтах-->
{page.data.map(({ astronaut }) => <li>{astronaut}</li>)}
</ul>

Это генерирует следующие страницы, по 2 элемента на странице:

  • /astronauts/1 - Страница 1: Отображает “Neil Armstrong” и “Buzz Aldrin”.
  • /astronauts/2 - Страница 2: отображает “Sally Ride” и “John Glenn”.

Когда вы используете функцию paginate(), каждой странице будут переданы данные через пропс page. Пропс page имеет множество полезных свойств, но вот основные из них:

  • page.data - массив, содержащий фрагмент данных страницы, который вы передали функции paginate().
  • page.url.next - ссылка на следующую страницу в наборе
  • page.url.prev - ссылка на предыдущую страницу в наборе
src/pages/astronauts/[page].astro
---
// Выводим на страницу тот же список объектов { astronaut }, что и в предыдущем примере
export async function getStaticPaths({ paginate }) { /* ... */ }
const { page } = Astro.props;
---
<h1>Page {page.currentPage}</h1>
<ul>
{page.data.map(({ astronaut }) => <li>{astronaut}</li>)}
</ul>
{page.url.prev ? <a href={page.url.prev}>Previous</a> : null}
{page.url.next ? <a href={page.url.next}>Next</a> : null}
interface Page<T = any> {
/** результат */
данные: T[];
/** метаданные */
/** счетчик первого элемента на странице, начиная с 0 */
start: number;
/** счетчик последнего элемента на странице, начиная с 0 */
end: number;
/** общее количество результатов */
total: number;
/** номер текущей страницы, начиная с 1 */
currentPage: number;
/** количество элементов на странице (по умолчанию: 25) */
size: number;
/** номер последней страницы */
lastPage: number;
url: {
/** url текущей страницы */
current: string;
/** url предыдущей страницы (если она есть) */
prev: string | undefined;
/** url следующей страницы (если она есть) */
next: string | undefined;
};
}

Более продвинутым вариантом использования пагинации является вложенная пагинация. Это когда пагинация сочетается с другими динамическими параметрами маршрута. Вы можете использовать вложенную пагинацию для группировки коллекции пагинаций по какому-либо свойству или тегу.

Например, если вы хотите сгруппировать посты в Markdown по какому-то тегу, вы можете использовать вложенную пагинацию, создав страницу /src/pages/[tag]/[page].astro, которая будет соответствовать следующим URLS:

  • /red/1 (tag=red)
  • /red/2 (tag=red)
  • /blue/1 (tag=blue)
  • /green/1 (tag=green)

Вложенная пагинация работает путем возврата массива результатов paginate() из getStaticPaths(), по одному для каждой группировки.

В следующем примере мы реализуем вложенную пагинацию для построения URL-адресов, перечисленных выше:

src/pages/[tag]/[page].astro
---
export async function getStaticPaths({ paginate }) {
const allTags = ['red', 'blue', 'green'];
const allPosts = await Astro.glob('../../posts/*.md');
// Для каждого тега возвращаем результат paginate().
// Убедитесь, что вы передали `{params: {tag}}` в `paginate()`.
// чтобы Astro знал, для какой группы тегов получен результат.
return allTags.flatMap((tag) => {
const filteredPosts = allPosts.filter((post) => post.frontmatter.tag === tag);
return paginate(filteredPosts, {
params: { tag },
pageSize: 10
});
});
}
const { page } = Astro.props;
const params = Astro.params;

Вы можете исключить страницы или каталоги из сборки, добавив к их именам символ подчеркивания (_). Файлы с префиксом _ не будут распознаны маршрутизатором и не будут помещены в каталог dist/.

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

В этом примере только src/pages/index.astro и src/pages/posts/post1.md будут построены как маршруты страниц и HTML-файлы.

  • Директорияsrc/pages/
    • Директория_hidden-directory/
      • page1.md
      • page2.md
    • _hidden-page.astro
    • index.astro
    • Директорияposts/
      • _SomeComponent.astro
      • _utils.js
      • post1.md