Сайт из документации, блога и лендинга редко нуждается в мегабайтах JavaScript. Но стоит добавить корзину или фильтр, как фреймворк тянет гидратацию на все страницы. Astro предлагает компромисс: HTML собирается заранее, а JavaScript загружается только для явно помеченных интерактивных блоков — островов.
Как работают острова
Компонент .astro выполняется на этапе сборки и превращается в HTML. Если внутри нужен React, Vue или Svelte, его подключают директивой client. Без директивы компонент отрендерится статически, а его скрипты в браузер не попадут.
- client:load — гидрировать сразу, для важных элементов.
- client:idle — после загрузки страницы, когда браузер свободен.
- client:visible — когда блок появился в зоне видимости.
- client:only — только на клиенте, без серверного HTML.
Чем позже гидрирование, тем меньше работы при первом рендере. Но у client:visible есть цена: до появления блока интерфейс не отвечает. Для кнопки «купить» это плохо, для виджета комментариев — нормально.
Граница острова проходит по компоненту, а не по странице. Внутри одного острова может быть целое дерево React-компонентов, и весь этот код загрузится вместе. Поэтому крупные виджеты делят на несколько независимых островов, чтобы не тянуть лишнее.
Создание проекта
Мастер задаёт имя, шаблон и настройки TypeScript. Минимальный шаблон не тащит лишних зависимостей.
npm create astro@latest blog -- --template minimal
cd blog
npm install
npm run dev
Разработка идёт на локальном сервере с горячей перезагрузкой, а сборка складывает готовый сайт в dist. Предпросмотр проверяет именно собранную версию, а не dev-режим.
npm run build
npm run preview
npx astro check
npx astro --version
Структура и маршруты
Файлы в src/pages становятся маршрутами. Динамические страницы используют квадратные скобки и getStaticPaths: на этапе сборки Astro вызывает функцию и создаёт по странице на каждый элемент.
---
export async function getStaticPaths() {
return [
{ params: { slug: 'pervaya' } },
{ params: { slug: 'vtoraya' } },
];
}
const { slug } = Astro.params;
---
Общий каркас выносят в layout: там заголовок, навигация, подвал и слот. Страницы подключают его и передают содержимое. Так проще держать метатеги и canonical в одном месте.
Контент-коллекции
Markdown-файлы удобно хранить в src/content и описывать схему. Схема проверяет поля на сборке: забытая дата или строка вместо массива тегов остановят билд, а не сломают страницу в проде.
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
const blog = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/blog' }),
schema: z.object({
title: z.string(),
date: z.coerce.date(),
draft: z.boolean().default(false),
}),
});
export const collections = { blog };
На странице коллекцию читают через getCollection, фильтруют черновики и сортируют по дате. Функция render отдаёт компонент и заголовки для оглавления.
---
import { getCollection } from 'astro:content';
const posts = (await getCollection('blog', ({ data }) => !data.draft))
.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
---
Если контент приходит из CMS, вместо glob используют загрузчик под API. Схема остаётся той же, поэтому шаблоны не меняются.
Остров с React
Интеграция ставится одной командой и добавляет зависимости. Дальше компонент импортируют и помечают директивой.
npx astro add react
npm run build
---
import Counter from '../components/Counter.jsx';
---
<Counter client:visible />
Такой остров скачает код только при прокрутке до блока. Если виджет нужен сразу, меняют директиву на client:load, но следят за размером бандла: каждая библиотека внутри острова едет в браузер целиком.
Картинки и производительность
Компонент Image из astro:assets оптимизирует изображения на сборке: меняет формат, режет размеры, добавляет width и height. Это убирает сдвиг вёрстки и лишние килобайты. Для локальных файлов достаточно импорта.
---
import { Image } from 'astro:assets';
import cover from '../assets/cover.jpg';
---
<Image src={cover} alt="Обложка" widths={[480, 960]} />
Шрифты подключают локально, а не через внешний CDN: меньше запросов и никаких сторонних соединений. Критические стили инлайнят, остальные грузят отдельно. Prefetch ускоряет переходы по ссылкам, но увеличивает трафик — включайте осознанно.
Сборка и публикация
Статический вариант — это каталог dist, который можно отдать любым веб-сервером. Кэширование хешированных файлов ставят надолго, а HTML — ненадолго, чтобы обновления доходили быстро.
server {
root /var/www/blog/dist;
index index.html;
error_page 404 /404.html;
location /_astro/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location / {
try_files $uri $uri/ =404;
}
}
Если нужен серверный рендеринг, подключают адаптер Node и запускают entry.mjs под systemd или в контейнере. Часть маршрутов можно оставить статическими, а личный кабинет рендерить на сервере. Перед публикацией прогоняйте сборку на отдельном стенде: так ошибки путей и кэша видны до того, как их увидят читатели.
Диагностика
Основные проблемы видны на сборке и в консоли браузера. astro check ловит типы в .astro и TypeScript, а предупреждения о гидратации обычно означают несовпадение разметки на сервере и клиенте.
- Страница пустая — проверьте base и пути к ассетам при деплое в подкаталог.
- Компонент не реагирует — забыта директива client.
- Дублирование стилей — разные версии интеграции.
- Медленный первый рендер — слишком много островов с client:load.
Смотрите размер JS на конкретной странице, а не общий бандл. Один тяжёлый виджет на главной может стоить дороже всего остального сайта.
Для проверки доступности прогоняйте страницы с отключённым JavaScript: контент должен оставаться читаемым, а навигация — работать. Это же упражнение показывает, какие блоки действительно зависят от гидратации, а какие можно оставить статическими навсегда.
Чек-лист
- Схема коллекций описывает все обязательные поля.
- Интерактивность только там, где она нужна.
- Картинки с размерами и современным форматом.
- Кэш настроен для статики и HTML по-разному.
- Сборка и preview проверены перед публикацией.
Итог
Astro хорошо подходит контентным сайтам: быстрый первый рендер, минимум JavaScript и понятная работа с Markdown. Острова позволяют добавлять интерактивность точечно, не переписывая всё на фреймворк. Начните с одной страницы и замерьте вес JS — обычно после этого желание тащить SPA на весь сайт пропадает.