Astro: страница статьи из записи коллекции
Коллекция содержит пять материалов с общими метаданными. Теперь свяжем каждую опубликованную запись с самостоятельной HTML-страницей. Динамический файл маршрута в статическом проекте не означает запрос к базе во время посещения. Он описывает, какие пути нужно подготовить заранее. Результат урока — понятная цепочка: запись, параметр маршрута, отрисованное тело Markdown.
Файл src/pages/articles/[id].astro содержит getStaticPaths. Функция получает коллекцию и исключает записи с draft: true. Затем для каждой записи возвращает параметр id и саму запись в props. Параметр должен совпадать с именем в квадратных скобках. Если вернуть slug вместо id, сходство смысла не поможет генератору понять нужный маршрут.
От записи к отдельной странице
Передача записи через props позволяет нижней части шаблона использовать именно тот объект, для которого сформирован путь. Не нужно снова искать запись по параметру и повторять фильтр. Такое решение делает связь наглядной: один элемент результата getStaticPaths определяет и адрес, и содержание. При увеличении коллекции это также снижает количество мест, где может разойтись логика.
render(entry) подготавливает компонент Content для Markdown-тела. Метаданные остаются в entry.data. Название вставляется в H1, а Content выводит абзацы и заголовки. Не подставляем сырое тело через самодельный innerHTML и не пытаемся разбирать Markdown в браузере. Статический режим позволяет выполнить эту работу заранее.
Формат сборки preserve выбран, чтобы файловые маршруты могли сохранить HTML-форму, а индексные страницы — каталожную. Для этого этапа ожидаются адреса вида /articles/markdown.html. Расширение и trailing slash являются частью договора конфигурации. Не угадывайте URL только по красивому ID: сравнивайте его с выбранным форматом вывода и реальным именем маршрута.
HTML пока повторяет общую оболочку в каждом шаблоне. Это временная форма, чтобы показать сам маршрут. В следующем уроке выделим layout. Сейчас полезно видеть полный документ: язык, кодировку, viewport, title и один основной заголовок. Наличие Markdown не освобождает страницу от обычных обязанностей HTML.
Фильтрация черновиков находится прямо в месте генерации путей. Позднее вынесем её в общую функцию и применим к каталогу и поиску. Если скрывать запись только в списке главной страницы, её самостоятельный URL всё ещё может быть создан. Для редакционного статуса важна согласованность всех каналов вывода, а не отсутствие одной ссылки.
Ожидаемый результат — пять самостоятельных страниц с соответствующими названиями и телом материала. При подготовке эти страницы не собраны и не открыты в учебном приложении. Чтобы будущий просмотр был содержательным, сравните две разные записи и прямую загрузку URL. Ошибки маршрута могут быть незаметны, если переходить только по одному удачно написанному адресу.
Не добавляем JavaScript-роутер для открытия статьи. Обычный документ подходит библиотеке: ссылки можно копировать, открывать отдельно и сохранять в истории. Если появится интерактивная часть, её допустимо добавить локально. Сам процесс чтения не должен требовать восстановления большого клиентского состояния после каждой навигации.
Полный файл src/pages/articles/[id].astro текущего этапа:
---
import { getCollection, render } from 'astro:content';
export async function getStaticPaths() {
return (await getCollection('lessons', ({ data }) => !data.draft)).map(entry => ({ params: { id: entry.id }, props: { entry } }));
}
const { entry } = Astro.props;
const { Content } = await render(entry);
---
<!doctype html><html lang="ru"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>{entry.data.title}</title></head><body><main><h1>{entry.data.title}</h1><Content /></main></body></html>
Разбор состояния библиотеки
Статический динамический маршрут всегда нуждается в конечном перечислении путей. Название dynamic здесь относится к параметру шаблона, а не к обещанию обслужить любой неизвестный ID после публикации. Если пользователь запросит /articles/unknown.html, заранее созданного документа не будет. Хостинг должен иметь собственную страницу ошибки, а не отправлять любой адрес на главную и скрывать отсутствие материала.
Функция getStaticPaths не получает название из URL как произвольный HTML. Она возвращает значения из контролируемой коллекции. Тем не менее ID следует рассматривать как устойчивый договор. Если loader начнёт формировать вложенные ID, одиночный параметр id может стать недостаточным и потребовать rest-маршрута. Наши пять имён не содержат разделителей, поэтому выбранная форма соответствует исходному набору.
Ошибка статьи может находиться в содержании или в маршрутизации. Неверный title изменит заголовок, но не обязательно сломает путь. Неправильный параметр может не создать документ вовсе. Поэтому будущий разбор должен разделять эти причины. Начните с наличия пути в перечислении, затем с переданной записи, затем с render и конечным HTML. Такой порядок помогает не пытаться исправить CSS там, где документ вообще не подготовлен.
Содержимое Markdown обрабатывается выбранной системой Astro. Не следует добавлять произвольные плагины ради соответствия случайному старому примеру. Каждый плагин меняет граф зависимостей и потенциальный результат обработки. В серии используем стандартный путь для ограниченного набора заголовков и абзацев. Расширенные таблицы, диаграммы или особые блоки потребуют нового явного договора и проверки результата.
Для самостоятельного варианта измените body одной записи, оставив её title и ID прежними. Должен измениться только соответствующий материал после следующей сборки. Затем переключите эту запись в draft true и рассмотрите список маршрутов. Так становятся видны две независимые операции: редактирование содержания существующей страницы и исключение страницы из публичного набора. В настоящем сайте защищённые исторические материалы нельзя скрывать таким способом без отдельного решения.
Сохранение обычных ссылок также помогает передаче. Другой автор может открыть статью напрямую и понять её место по общему шаблону, не воспроизводя предыдущие клики в приложении. Именно поэтому каждый этап статьи должен объяснять необходимые определения и исходные файлы. Серия даёт последовательность обучения, но не превращает самостоятельные страницы в недоступные без клиентского состояния фрагменты.
У файла endpoint и файла страницы разное назначение. Здесь создаётся HTML-документ статьи через .astro, а будущий JSON-индекс получит .ts endpoint. Не подменяйте одно другим только ради похожего имени. В обоих случаях static требует понятного набора результатов, но разметка, тип содержимого и потребитель различаются. Такая граница помогает разобрать ошибку, когда посетителю вместо страницы возвращается технический ресурс.
Условия для самостоятельного наблюдения
Это план будущего разбора, а не журнал выполненных проверок. Рассмотрите одинаковые исходные данные и сопоставьте действие с ожидаемым состоянием.
| Условие | Ожидаемое состояние и его граница |
|---|---|
| Пять публичных записей | GetStaticPaths должен подготовить пять соответствующих самостоятельных маршрутов. |
| Черновая запись | Её путь исключается на этапе перечисления страниц, а не только из главного списка. |
| Имя параметра неверно | Параметр должен точно соответствовать id в имени файла маршрута. |
| Название изменено | Меняется H1 и title, но ID и адрес остаются прежними. |
| Прямая загрузка URL | Статья должна читаться самостоятельно без предварительного посещения главной страницы. |
| Нет страницы-маршрута | Коллекция остаётся данными и не публикует записи только фактом существования файлов. |
Оглавление серии, курсы ProfessorWeb и полный архив исходников. Для этого урока выберите lesson-03.
Первичные источники: Collections, Routing.
Редакционный статус: локальный черновик. Установка, учебные программы, браузерные сценарии, тесты и публикация не выполнялись. Описанные эффекты ожидаемые.