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

3. Astro-компоненты (.astro)

Компонент .astro — это основная единица UI в Astro. По синтаксису он напоминает обычный HTML-файл, расширенный возможностями JavaScript и CSS, но с ключевым отличием: весь JavaScript выполняется только на сервере.

Каждый .astro файл состоит из трёх частей:

---
// 1. FRONTMATTER — серверный JavaScript
// Всё здесь выполняется при сборке или на сервере
import Component from './Component.astro';
const message = 'Привет, Astro!';
const items = [1, 2, 3];
---
<!-- 2. ШАБЛОН — HTML с выражениями -->
<div class="container">
<h1>{message}</h1>
<ul>
{items.map(item => <li>{item}</li>)}
</ul>
<Component />
</div>
<style>
/* 3. СТИЛИ — изолированные CSS */
.container {
padding: 1rem;
}
</style>

Три разделителя --- обрамляют frontmatter — это синтаксис, знакомый по Markdown.

Код в frontmatter выполняется только на сервере. Здесь можно:

---
// Импорты
import Layout from '../layouts/Layout.astro';
import Button from '../components/Button.astro';
import type { ImageMetadata } from 'astro';
// Работа с данными
const response = await fetch('https://api.example.com/posts');
const posts = await response.json();
// Условная логика
const isLoggedIn = Astro.cookies.get('session')?.value;
const user = isLoggedIn ? await getUser() : null;
// Работа с пропсами
const { title, description = 'По умолчанию' } = Astro.props;
// Работа с URL
const currentPath = Astro.url.pathname;
---

Важно: этот код не попадёт в браузер. Даже console.log в frontmatter выводит сообщение только в терминале, не в браузере.

Шаблон — это расширенный HTML. В нём можно использовать JSX-подобные выражения:

---
const name = 'Мир';
const isAdmin = true;
const colors = ['красный', 'зелёный', 'синий'];
const url = 'https://astro.build';
---
<!-- Вывод переменных -->
<h1>Привет, {name}!</h1>
<!-- Условный рендеринг -->
{isAdmin && <span class="badge">Администратор</span>}
{isAdmin ? <p>Панель управления</p> : <p>Нет доступа</p>}
<!-- Рендеринг списков -->
<ul>
{colors.map(color => (
<li class="color-item">{color}</li>
))}
</ul>
<!-- Атрибуты из переменных -->
<a href={url} target="_blank">Сайт Astro</a>
<!-- Класс из условия -->
<div class:list={['base', isAdmin && 'admin', 'container']}>
Контент
</div>

Компоненты Astro принимают пропсы через Astro.props. Для типизации используйте интерфейс:

---
// Определяем типы пропсов
interface Props {
title: string;
subtitle?: string;
count: number;
variant?: 'primary' | 'secondary';
}
// Деструктурируем пропсы
const { title, subtitle, count, variant = 'primary' } = Astro.props;
---
<div class={'card card--' + variant}>
<h2>{title}</h2>
{subtitle && <p>{subtitle}</p>}
<span class="count">{count}</span>
</div>

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

---
import Card from '../components/Card.astro';
---
<Card title="Привет" count={42} variant="primary" />
<Card title="Мир" subtitle="Описание" count={7} />

Слоты позволяют передавать HTML-контент в компонент — аналог children в React:

Button.astro
---
const { variant = 'primary' } = Astro.props;
---
<button class={'btn btn--' + variant}>
<slot /> <!-- Сюда вставится переданный контент -->
</button>
<!-- Использование -->
<Button variant="primary">
Нажми меня 🚀
</Button>
<Button variant="secondary">
<span>Иконка</span>
Текст с иконкой
</Button>
Card.astro
<div class="card">
<header>
<slot name="header" /> <!-- Именованный слот -->
</header>
<main>
<slot /> <!-- Дефолтный слот -->
</main>
<footer>
<slot name="footer" />
</footer>
</div>
<Card>
<h2 slot="header">Заголовок карточки</h2>
<p>Основной контент карточки</p>
<Button slot="footer">Подробнее</Button>
</Card>

CSS в <style> блоке автоматически изолируется для текущего компонента:

<div class="title">Заголовок</div>
<p class="text">Текст</p>
<style>
/* Эти стили применяются ТОЛЬКО к этому компоненту */
.title {
color: #FF5D01;
font-size: 2rem;
}
.text {
color: #64748b;
}
</style>

Astro добавляет уникальный атрибут data-astro-cid-xxxxxxxx к элементам, обеспечивая изоляцию стилей без CSS Modules или styled-components.

Для глобальных стилей используйте :global():

<style>
/* Только для этого компонента */
.button { background: #FF5D01; }
/* Глобальный стиль */
:global(.reset) { margin: 0; padding: 0; }
</style>

В frontmatter доступен глобальный объект Astro:

---
// URL текущей страницы
const url = Astro.url; // URL object
const path = Astro.url.pathname; // '/blog/post-1'
// Параметры запроса
const params = Astro.params; // { slug: 'post-1' }
// Куки
const cookie = Astro.cookies.get('name')?.value;
// Заголовки запроса (только при SSR)
const userAgent = Astro.request.headers.get('user-agent');
// Перенаправление
if (!isLoggedIn) {
return Astro.redirect('/login');
}
---

Исследуйте разные примеры .astro компонентов. Переключайтесь между вкладками: