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

Макеты

Макеты (layouts) — это компоненты Astro, предназначенные для создания переиспользуемой структуры интерфейса, например шаблона страницы.

Традиционно мы называем «макетами» компоненты, которые содержат общие элементы страниц: шапку, меню навигации и подвал. Типичный макет Astro предоставляет страницам (Astro, Markdown или MDX):

  • оболочку страницы (теги <html>, <head> и <body>);
  • <slot />, определяющий место для вставки контента конкретной страницы.

При этом макеты — это обычные компоненты Astro! Они могут принимать пропсы, импортировать другие компоненты, использовать UI-фреймворки и скрипты. Они даже не обязательно должны содержать полную оболочку страницы и могут использоваться как частичные UI-шаблоны.

Однако, если макет всё же содержит оболочку страницы, тег <html> должен быть родителем для всех остальных элементов компонента.

Макеты принято размещать в папке src/layouts/, но это не обязательное требование — вы можете хранить их где угодно. Можно даже размещать их рядом со страницами, добавляя префикс _ к именам файлов, чтобы они не стали отдельными маршрутами.

src/layouts/MySiteLayout.astro
---
import BaseHead from '../components/BaseHead.astro';
import Footer from '../components/Footer.astro';
const { title } = Astro.props;
---
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<BaseHead title={title}/>
</head>
<body>
<nav>
<a href="/">Главная</a>
<a href="/posts">Посты</a>
<a href="/contact">Контакты</a>
</nav>
<h1>{title}</h1>
<article>
<slot /> <!-- контент страницы попадет сюда -->
</article>
<Footer />
</body>
<style>
h1 {
font-size: 2rem;
}
</style>
</html>

Использование макета:

src/pages/index.astro
---
import MySiteLayout from '../layouts/MySiteLayout.astro';
---
<MySiteLayout title="Главная страница">
<p>Контент моей страницы, обёрнутый в макет!</p>
</MySiteLayout>
Узнайте больше о слотах.

Любой макет можно сделать типобезопасным, определив интерфейс для его пропсов:

src/components/MyLayout.astro
---
interface Props {
title: string;
description: string;
publishDate: string;
viewCount: number;
}
const { title, description, publishDate, viewCount } = Astro.props;
---
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="description" content={description}>
<title>{title}</title>
</head>
<body>
<header>
<p>Опубликовано: {publishDate}</p>
<p>Просмотров: {viewCount}</p>
</header>
<main>
<slot />
</main>
</body>
</html>

Макеты особенно полезны для отдельных страниц Markdown, которые сами по себе не имеют форматирования.

Astro поддерживает специальное свойство layout в блоке метаданных (frontmatter) для отдельных .md файлов в папке src/pages/. С его помощью можно указать, какой .astro компонент использовать в качестве макета. Этот компонент обеспечит наличие тегов <head>, стилей и общей структуры страницы.

src/pages/page.md
---
layout: ../layouts/BlogPostLayout.astro
title: "Привет, мир!"
author: "Мэтью Филлипс"
date: "09 авг 2022"
---
Все свойства из этого блока доступны макету через Astro.props.
Свойство `layout` — единственное, которое Astro обрабатывает особым образом.

Типичный макет для Markdown-страницы включает:

  1. Пропс frontmatter для доступа к метаданным страницы.
  2. Слот по умолчанию <slot /> для рендеринга самого контента.
src/layouts/BlogPostLayout.astro
---
// 1. Пропс frontmatter дает доступ к метаданным
const { frontmatter } = Astro.props;
---
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8">
<title>{frontmatter.title}</title>
</head>
<body>
<!-- Общие элементы интерфейса -->
<h1>{frontmatter.title} (автор: {frontmatter.author})</h1>
<!-- 2. Сюда попадет скомпилированный HTML из Markdown -->
<slot />
<p>Дата публикации: {frontmatter.date}</p>
</body>
</html>

Вы можете типизировать пропсы макета с помощью хелпера MarkdownLayoutProps:

src/layouts/BlogPostLayout.astro
---
import type { MarkdownLayoutProps } from 'astro';
type Props = MarkdownLayoutProps<{
// Типизация свойств из frontmatter
title: string;
author: string;
date: string;
}>;
const { frontmatter, url } = Astro.props;
---
<html>
<head>
<meta charset="utf-8">
<link rel="canonical" href={new URL(url, Astro.site).pathname}>
<title>{frontmatter.title}</title>
</head>
<body>
<h1>{frontmatter.title} от {frontmatter.author}</h1>
<slot />
<p>Дата: {frontmatter.date}</p>
</body>
</html>

Макету Markdown доступны следующие данные через Astro.props:

  • file — абсолютный путь к файлу (например, /home/user/projects/.../file.md).
  • url — URL страницы (например, /ru/guides/markdown-content).
  • frontmatter — все метаданные из документа Markdown или MDX.
    • frontmatter.file — то же, что и свойство file верхнего уровня.
    • frontmatter.url — то же, что и свойство url верхнего уровня.
  • headings — список заголовков (h1 -> h6) в документе с их метаданными. Тип: { depth: number; slug: string; text: string }[].
  • rawContent() — функция, возвращающая исходный Markdown-текст в виде строки.
  • compiledContent() — асинхронная функция, возвращающая скомпилированный HTML-код в виде строки.

Свойство layout также работает в файлах MDX.

Однако в MDX можно импортировать макет как обычный компонент. В этом случае он не получит пропсы автоматически — их нужно передать явно:

src/pages/posts/first-post.mdx
---
layout: ../../layouts/BaseLayout.astro
title: 'Мой первый пост на MDX'
publishDate: '21 сентября 2022'
---
import BaseLayout from '../../layouts/BaseLayout.astro';
export function fancyJsHelper() {
return "Это можно сделать только в MDX!";
}
<BaseLayout title={frontmatter.title} fancyJsHelper={fancyJsHelper}>
Добро пожаловать в мой новый блог на Astro с поддержкой MDX!
</BaseLayout>

В макете контент MDX попадет в слот <slot />:

src/layouts/BaseLayout.astro
---
const { title, fancyJsHelper } = Astro.props;
---
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<h1>{title}</h1>
<slot /> <!-- контент вставится сюда -->
<p>{fancyJsHelper()}</p>
</body>
</html>
Узнайте больше в нашем руководстве по Markdown и MDX.

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

Например, BlogPostLayout.astro может отвечать за стиль статьи, а общий BaseLayout.astro — за навигацию, подвал и SEO.

src/layouts/BlogPostLayout.astro
---
import BaseLayout from './BaseLayout.astro';
const { frontmatter } = Astro.props;
---
<BaseLayout url={frontmatter.url}>
<h1>{frontmatter.title}</h1>
<h2>Автор: {frontmatter.author}</h2>
<slot />
</BaseLayout>