Все статьи

Эволюция персонального сайта: от React Discord-визитки до островной архитектуры Astro 5

astroreactarchitectureperformancefrontendtypescripttailwind

Эволюция персонального сайта: от React Discord-визитки до островной архитектуры Astro 5

Персональный сайт разработчика редко остается статичным артефактом: он непрерывно эволюционирует вместе с профессиональным опытом, стеком и требованиями к производительности. Однако фундаментальный выбор архитектуры определяет ключевые свойства ресурса — скорость первой отрисовки (FCP), точность поискового индексирования (SEO), надежность предпросмотра в социальных сетях (OpenGraph) и удобство долгосрочной поддержки архива технических кейсов.

В данном материале разобран путь развития персонального сайта через три поколения: от простого HTML+CSS, через интерактивную визитку React-Discord-Business-Card и до современной платформы OneHeka Portfolio на базе Astro 5 и островной архитектуры (Islands Architecture).


1. Хронология поколений: от статики к островам

Каждая итерация решала задачи своего времени, постепенно выявляя ограничения выбранного стека:

[ Поколение 1: Статический HTML + CSS ]
  Простая посадочная страница, минимальный функционал, отсутствие интерактивности

       ▼ (Потребность в интерактиве и эстетике Discord)
[ Поколение 2: React Discord Business Card (SPA) ]
  React, Vite/CRA, CSS/Framer Motion, имитация Discord-профиля

       ▼ (Масштабирование: блог, кейсы, i18n, оптимизация FCP и SEO)
[ Поколение 3: OneHeka Portfolio (Astro 5 + React 19 Islands) ]
  Astro 5, Tailwind CSS v4, React 19 (острова), Content Collections,
  поддержка 4 языков (RTL/LTR), 100/100 Lighthouse

Сравнительный срез трех поколений

Параметр Поколение 1: HTML+CSS Поколение 2: React Discord Card Поколение 3: OneHeka Portfolio
Стек Чистый HTML5, CSS3 React, Vite, Tailwind, Framer Motion Astro 5, React 19, Tailwind v4, Zod
Рендеринг Статический файл Клиентский SPA (CSR) Статическая генерация (SSG) + Острова
Базовый вес страницы ~5–10 КБ ~250–400 КБ (JS-бандл) ~15–30 КБ (Чистый HTML + CSS)
First Contentful Paint (FCP) 0.2 с 1.4–2.4 с 0.1–0.3 с
Интерактивность Отсутствует Полная (но тяжелая) Точечная (через директивы client:*)
SEO & OpenGraph Статический Проблемный (пустой <div id="root">) Нативный SSG для 4 языков
Масштабируемость контента Ручная верстка Громоздкий стейт и роуты Типобезопасные Content Collections

2. Поколение 2: React Discord Business Card и лимиты SPA

Вторая версия проекта создавалась с фокусом на визуальную аутентичность интерфейса Discord: анимированная плашка профиля, бейджи, статус активности, интерактивные всплывающие карточки и контактные ссылки.

Архитектурные ограничения классического React SPA

Хотя решение выглядело эффектно, при попытке превратить визитку в полноценное портфолио с блогом и техническими разборами проявились ключевые недостатки Single Page Application:

  1. Задержка первого отображения (FCP & TTI): Браузер получал пустой HTML-каркас (<div id="root"></div>) и был вынужден ждать загрузки, парсинга и исполнения всего JS-бандла React перед тем, как пользователь увидит первый байт контента.
  2. Ненадежный OpenGraph и SEO: Поисковые краулеры и парсеры превью в мессенджерах (Telegram, Discord, Twitter) часто считывали пустую страницу без динамически сформированных метаданных конкретной статьи.
  3. Раздувание кодовой базы: Добавление многостраничности через react-router-dom, локализации через громоздкие JSON-словари и ленивых импортов (React.lazy) неоправданно усложняло проект.

3. Парадигма Astro 5: Островная архитектура и Zero-JS

При проектировании третьей версии — OneHeka Portfolio — был совершен переход на Astro 5. В основу легла концепция Astro Islands:

  • По умолчанию все страницы (статьи, списки проектов, шапка, футер) компилируются во время сборки в статический HTML и CSS без клиентского рантайма JavaScript.
  • Компоненты React подключаются точечно как изолированные «острова» с явным указанием стратегии гидратации:
---
import { TableOfContents } from "@/components/molecules/table-of-contents";
import { ShareButton } from "@/components/molecules/share-button";
import { CommandMenu } from "@/components/molecules/command-menu";
import MainLayout from "@/layouts/main.astro";

const { headings, lang, title, description } = Astro.props;
---

<MainLayout title={title} description={description} lang={lang}>
    <!-- Статический контент: чистый семантический HTML, 0 КБ JS -->
    <article class='prose min-w-0 max-w-full'>
        <slot />
    </article>

    <!-- Интерактивные острова React гидратируются независимо -->
    <TableOfContents headings={headings} lang={lang} client:load />
    <ShareButton lang={lang} client:load />
    <CommandMenu lang={lang} client:idle />
</MainLayout>

Стратегии гидратации островов:

  • client:load — компонент гидратируется сразу при загрузке (глобальный скроллбар CustomScrollbar, оглавление TableOfContents).
  • client:idle — гидратация откладывается до освобождения основного потока (палитра команд CommandMenu, кнопка шаринга).
  • client:visible — скрипты загружаются только при попадании компонента в область видимости (viewport).

4. Типобезопасный контент: Content Collections и Zod

Управление статьями и кейсами реализовано через встроенный загрузчик Content Collections (src/content.config.ts), гарантирующий строгую валидацию схем метаданных на этапе компиляции:

import { defineCollection, z } from "astro:content";
import { glob } from "astro/loaders";

const blog = defineCollection({
    loader: glob({ pattern: '**/*.{md,mdx}', base: './src/content/blog' }),
    schema: z.object({
        title: z.string(),
        description: z.string(),
        pubDate: z.coerce.date(),
        updatedDate: z.coerce.date().optional(),
        tags: z.array(z.string()).optional().default([]),
        lang: z.enum(['ru', 'en', 'zh', 'ae']).default('ru')
    })
})

export const collections = { blog }

Любая ошибка в метаданных Markdown (отсутствие обязательного поля, неверный формат даты или неподдерживаемый язык) моментально прерывает сборку с указанием конкретной строки, гарантируя целостность структуры сайта в продакшене.


5. Интернационализация (i18n): 4 языка и нативный RTL

В OneHeka Portfolio реализована нативная многоязычность:

  • 🇷🇺 Русский (ru)
  • 🇬🇧 Английский (en)
  • 🇨🇳 Китайский (zh)
  • 🇦🇪 Арабский (ae) — с полноценной правосторонней версткой (dir="rtl").

В отличие от SPA, где локали подгружаются через клиентские хранилища, Astro генерирует полностью статические страницы для каждого языка со сформированными тегами hreflang и каноническими ссылками.


6. Современный стек: Tailwind CSS v4, Geist Font и React Aria

Визуальная экосистема сайта построена на современных инженерных решениях:

  • Tailwind CSS v4 с компилятором на базе Vite, обеспечивающим молниеносный HMR.
  • Geist Variable Font от Vercel для идеальной четкости текста и читаемости кода.
  • React Aria Components для гарантированной доступности (a11y), поддержки скринридеров и фокус-менеджмента.
  • Палитра команд Cmd+K на базе cmdk с автоопределением операционной системы (Mac / Windows) и плавной модальной анимацией.

7. Ключевые инженерные выводы

  1. SPA не подходит для контентных ресурсов: Использование чистого клиентского React для блогов и портфолио неоправданно раздувает бандл и ухудшает FCP.
  2. Островная архитектура — золотой стандарт: Astro 5 объединяет привычный DX экосистемы React с производительностью и легкостью чистого HTML.
  3. Типобезопасность от кода до контента: Использование Zod для схем статей и TypeScript для компонентов исключает класс ошибок с битыми данными еще на этапе сборки.