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

5. Лейауты

Макеты (Layouts) в Astro — это специальные компоненты, предназначенные для оборачивания страниц общей структурой. Вместо того чтобы повторять <html>, <head>, навигацию и футер на каждой странице, вы выносите их в макет и переиспользуете.

Без макетов каждая страница содержала бы дублирование:

---
// src/pages/about.astro — БЕЗ макета (плохой подход)
---
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>О нас</title>
<link rel="stylesheet" href="/styles/global.css" />
</head>
<body>
<nav><!-- повторяется на каждой странице --></nav>
<main>
<h1>О нас</h1>
</main>
<footer><!-- повторяется на каждой странице --></footer>
</body>
</html>

С макетами структура разделяется правильно.

src/layouts/BaseLayout.astro
---
interface Props {
title: string;
description?: string;
}
const { title, description = 'Мой Astro сайт' } = Astro.props;
---
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<meta name="description" content={description} />
<title>{title} | Мой сайт</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
</head>
<body>
<nav>
<a href="/">Главная</a>
<a href="/blog">Блог</a>
<a href="/about">О нас</a>
</nav>
<main>
<slot /> <!-- Сюда вставится контент страницы -->
</main>
<footer>
<p>© 2024 Мой сайт</p>
</footer>
</body>
</html>
src/pages/about.astro
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="О нас" description="Информация о нашей команде">
<h1>О нас</h1>
<p>Мы создаём крутые сайты на Astro!</p>
</BaseLayout>

Весь контент между тегами <BaseLayout> попадёт в <slot /> макета.

Один <slot /> — это хорошо, но иногда нужно несколько областей для контента:

src/layouts/BlogLayout.astro
---
import BaseLayout from './BaseLayout.astro';
const { title, author, date } = Astro.props;
---
<BaseLayout title={title}>
<article>
<header>
<h1>{title}</h1>
<div class="meta">
<slot name="author-info" /> <!-- Именованный слот для автора -->
</div>
</header>
<div class="content">
<slot /> <!-- Основной контент статьи -->
</div>
<aside>
<slot name="sidebar" /> <!-- Слот для боковой панели -->
</aside>
</article>
</BaseLayout>
src/pages/blog/my-post.astro
---
import BlogLayout from '../../layouts/BlogLayout.astro';
---
<BlogLayout title="Моя статья">
<!-- Контент для slot name="author-info" -->
<div slot="author-info">
<img src="/avatar.jpg" alt="Автор" />
<span>Алексей Иванов</span>
</div>
<!-- Основной контент (дефолтный slot) -->
<p>Это основной текст статьи...</p>
<p>Продолжение...</p>
<!-- Контент для slot name="sidebar" -->
<nav slot="sidebar">
<h3>Содержание</h3>
<ul>
<li><a href="#intro">Введение</a></li>
<li><a href="#main">Основная часть</a></li>
</ul>
</nav>
</BlogLayout>

Макеты могут быть вложены друг в друга — это типичный паттерн:

BaseLayout.astro
└── BlogLayout.astro
└── PostLayout.astro
└── post.astro (страница)
src/layouts/PostLayout.astro
---
import BlogLayout from './BlogLayout.astro';
interface Props {
title: string;
pubDate: Date;
tags: string[];
}
const { title, pubDate, tags } = Astro.props;
---
<BlogLayout title={title}>
<div slot="author-info">
<time>{pubDate.toLocaleDateString('ru-RU')}</time>
{tags.map(tag => <span class="tag">#{tag}</span>)}
</div>
<slot /> <!-- Контент конкретной статьи -->
</BlogLayout>

Markdown-страницы могут использовать макеты через frontmatter:

src/pages/blog/my-post.md
---
layout: ../../layouts/PostLayout.astro
title: 'Введение в Astro'
pubDate: 2024-01-15
tags: ['astro', 'webdev', 'javascript']
---
# Введение в Astro
Этот контент будет вставлен в `<slot />` PostLayout.

В Astro 3+, для MDX используйте import:

post.mdx
import PostLayout from '../../layouts/PostLayout.astro'
export const frontmatter = {
title: 'Введение в Astro',
pubDate: new Date('2024-01-15'),
}
<PostLayout {...frontmatter}>
# Введение в Astro
Контент MDX страницы...
</PostLayout>

Макеты могут принимать пропсы от Markdown через Astro.props.frontmatter:

src/layouts/MarkdownLayout.astro
---
const { frontmatter } = Astro.props;
// frontmatter содержит все данные из YAML frontmatter markdown-файла
const { title, author, tags } = frontmatter;
---
<html>
<head><title>{title}</title></head>
<body>
<h1>{title}</h1>
<p>Автор: {author}</p>
<slot />
</body>
</html>

Посмотрите как контент проходит через вложенные макеты: