6. Архитектура Islands
Islands Architecture — это архитектурный паттерн для веб-приложений, предложенный Katie Sylor-Miller и популяризованный Jason Miller (создатель Preact). Astro стал первым крупным фреймворком, реализовавшим этот паттерн как основную концепцию.
Идея проста и гениальна: большинство страниц на большинстве сайтов статичны, и только небольшие, изолированные области требуют интерактивности. Зачем загружать JavaScript для всей страницы, если можно загрузить его только там, где он действительно нужен?
Проблема: Full-Page Hydration
Заголовок раздела «Проблема: Full-Page Hydration»В традиционных React/Next.js/Nuxt приложениях используется полная гидратация:
- Сервер рендерит HTML-страницу
- Браузер загружает весь JavaScript-бандл (часто 200KB — 1MB+)
- React “гидратирует” всю страницу — воссоздаёт дерево компонентов в памяти
- Страница становится интерактивной
Проблема: даже статичный текст требует загрузки и выполнения JavaScript для гидратации. Это называется “налогом на JavaScript”.
Типичная страница блога — чего хотим:┌─────────────────────────────┐│ Шапка (Nav) [статично] │ 0 KB JS├─────────────────────────────┤│ Заголовок [статично] │ 0 KB JS│ Текст статьи [статично] │ 0 KB JS│ Изображения [статично] │ 0 KB JS├─────────────────────────────┤│ Форма подписки [динамично]│ ~15 KB JS├─────────────────────────────┤│ Комментарии [динамично]│ ~30 KB JS├─────────────────────────────┤│ Футер [статично] │ 0 KB JS└─────────────────────────────┘ ИТОГО: ~45 KB JS
Реальность без Islands (Next.js по умолчанию):Вся страница = ~300 KB JS для гидратацииРешение: Острова (Islands)
Заголовок раздела «Решение: Острова (Islands)»С Islands Architecture только интерактивные компоненты получают JavaScript:
┌─────────────────────────────┐│ Шапка (Nav) ░░░░░░░░░░ │ ← Чистый HTML├─────────────────────────────┤│ Заголовок ░░░░░░░░░░ │ ← Чистый HTML│ Текст статьи ░░░░░░░░░░ │ ← Чистый HTML├─────────────────────────────┤│ ┌─ 🏝️ ОСТРОВ ─────────────┐ ││ │ Форма подписки │ │ ← ~15 KB JS│ └─────────────────────────┘ │├─────────────────────────────┤│ ┌─ 🏝️ ОСТРОВ ─────────────┐ ││ │ Комментарии │ │ ← ~30 KB JS│ └─────────────────────────┘ │├─────────────────────────────┤│ Футер ░░░░░░░░░░ │ ← Чистый HTML└─────────────────────────────┘ ИТОГО: ~45 KB JS (только острова!)Результат: браузер загружает только тот JavaScript, который действительно нужен.
Директивы клиентской гидратации
Заголовок раздела «Директивы клиентской гидратации»Astro управляет гидратацией через client-директивы:
---import Counter from '../components/Counter.jsx';import SearchBar from '../components/SearchBar.jsx';import HeavyChart from '../components/HeavyChart.jsx';import Tooltip from '../components/Tooltip.jsx';import MobileMenu from '../components/MobileMenu.jsx';---
<!-- Гидратировать сразу при загрузке страницы --><SearchBar client:load />
<!-- Гидратировать когда браузер не занят (idle) --><!-- Отлично для некритичных компонентов --><Counter client:idle />
<!-- Гидратировать когда компонент виден во viewport --><!-- Идеально для контента ниже fold --><HeavyChart client:visible />
<!-- Гидратировать при совпадении media query --><MobileMenu client:media="(max-width: 640px)" />
<!-- Гидратировать только на клиенте (без SSR) --><SomeDynamicWidget client:only="react" />
<!-- Статичный компонент — НИКАКОГО JavaScript --><Tooltip />Когда использовать каждую директиву
Заголовок раздела «Когда использовать каждую директиву»| Директива | Когда применять |
|---|---|
client:load | Критически важная интерактивность (навигация, форма авторизации) |
client:idle | Некритичные виджеты (счётчик, лайки) |
client:visible | Контент ниже fold (комментарии, heavy charts) |
client:media | Компоненты для конкретных экранов |
client:only | Компоненты без серверного рендеринга |
| (без директивы) | Полностью статичный HTML, 0 KB JS |
Острова с разными фреймворками
Заголовок раздела «Острова с разными фреймворками»Astro позволяет смешивать фреймворки — каждый остров может использовать свой:
---import ReactSearch from './SearchBar.jsx'; // Reactimport VueComments from './Comments.vue'; // Vueimport SvelteCounter from './Counter.svelte'; // Svelteimport StaticCard from './Card.astro'; // Astro (без JS)---
<StaticCard /> <!-- 0 KB JS --><ReactSearch client:load /> <!-- React JS --><VueComments client:visible /> <!-- Vue JS --><SvelteCounter client:idle /> <!-- Svelte JS -->Каждый остров — это изолированный микроприложение, независимое от других.
Практический пример: страница блога
Заголовок раздела «Практический пример: страница блога»---import { getCollection } from 'astro:content';import Layout from '../../layouts/BlogLayout.astro';import LikeButton from '../../components/LikeButton.jsx'; // Reactimport Comments from '../../components/Comments.vue'; // Vueimport Newsletter from '../../components/Newsletter.svelte'; // Svelte
export async function getStaticPaths() { const posts = await getCollection('blog'); return posts.map(post => ({ params: { slug: post.slug }, props: { post } }));}
const { post } = Astro.props;const { Content } = await post.render();---
<Layout title={post.data.title}> <!-- Статичная часть — 0 KB JS --> <h1>{post.data.title}</h1> <time>{post.data.date.toLocaleDateString()}</time>
<!-- Markdown контент — 0 KB JS --> <Content />
<!-- Острова — JS только здесь --> <LikeButton postId={post.id} client:visible /> <Newsletter client:idle /> <Comments postSlug={post.slug} client:visible /></Layout>Производительность: реальные цифры
Заголовок раздела «Производительность: реальные цифры»Сравнение страницы блога на разных фреймворках:
| Метрика | Gatsby | Next.js | Astro (Islands) |
|---|---|---|---|
| JS на клиенте | ~300 KB | ~180 KB | ~5-50 KB |
| Time to Interactive | 4.2s | 2.8s | 0.8s |
| Lighthouse Score | 75-85 | 80-90 | 95-100 |
| LCP | 3.1s | 2.2s | 0.9s |
Интерактивный визуализатор островов
Заголовок раздела «Интерактивный визуализатор островов»Посмотрите как работает Islands Architecture. Переключайте компоненты между “статичным” и “островом”: