Реалізація Islands Architecture для веб-застосунку

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація Islands Architecture для веб-застосунку
Складна
~3-5 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація Islands Architecture для веб-застосунку

Islands Architecture — архітектурний паттерн, де сторінка складається зі статичного HTML-океану з ізольованими «островами» інтерактивності. Кожний остров — незалежний компонент з власним JS, який гідрується окремо, не знаючи про сусідні острови.

Концепцію описав Джейсон Міллер (Preact) у 2020 році. Сьогодні це основа Astro, Marko та частина підходу Qwik.

Архітектурна модель

Статичний HTML (сервер, нулевий JS):
┌─────────────────────────────────────┐
│ Header                              │  ← HTML
│  Logo | Nav links | ...             │
├─────────────────────────────────────┤
│ Hero section                        │  ← HTML
│  H1, зображення, CTA               │
├──────────────┬──────────────────────┤
│ Article text │  🏝️ Island:          │  ← JS тільки для острова
│  (HTML)      │  TableOfContents.tsx │
│              │  (sticky, highlight) │
├──────────────┴──────────────────────┤
│ 🏝️ Island: CommentSection.tsx       │  ← JS тільки для острова
│  (React, завантажується при скролі) │
├─────────────────────────────────────┤
│ Footer                              │  ← HTML
└─────────────────────────────────────┘

Результат: JS грузиться тільки для 2 островів, не для всієї сторінки

Реалізація в Astro

Astro — перший фреймворк з нативною підтримкою Islands:

---
// src/pages/blog/[slug].astro
import type { GetStaticPaths } from 'astro';
import { getCollection } from 'astro:content';

// Серверні компоненти — .astro файли без JS на клієнті
import BaseLayout from '@/layouts/BaseLayout.astro';
import ArticleHero from '@/components/ArticleHero.astro';
import Prose from '@/components/Prose.astro';

// Острови — React/Vue/Svelte компоненти з директивами
import TableOfContents from '@/islands/TableOfContents.tsx';
import CommentSection from '@/islands/CommentSection.tsx';
import ShareButtons from '@/islands/ShareButtons.svelte';
import NewsletterSignup from '@/islands/NewsletterSignup.vue';

export const getStaticPaths: GetStaticPaths = async () => {
  const posts = await getCollection('blog', p => !p.data.draft);
  return posts.map(post => ({ params: { slug: post.slug }, props: { post } }));
};

const { post } = Astro.props;
const { Content, headings } = await post.render();
---

<BaseLayout title={post.data.title} description={post.data.description}>
  <ArticleHero post={post} />

  <div class="article-layout">
    <!-- Остров 1: інтерактивне оглавління -->
    <!-- Завантажується тільки коли браузер проставає -->
    <TableOfContents headings={headings} client:idle />

    <article>
      <Prose>
        <Content />
      </Prose>
    </article>
  </div>

  <!-- Остров 2: кнопки шарингу -->
  <!-- Завантажується тільки при появленні у viewport -->
  <ShareButtons
    url={Astro.url.href}
    title={post.data.title}
    client:visible
  />

  <!-- Остров 3: коментарі -->
  <!-- Завантажується тільки при появленні у viewport, затримка 500ms -->
  <CommentSection
    articleId={post.id}
    client:visible={{ rootMargin: '0px 0px 200px 0px' }}
  />

  <!-- Остров 4: підписка на розсилку -->
  <!-- Завантажується негайно — в fold -->
  <NewsletterSignup client:load />
</BaseLayout>

Комунікація між островами

Острови ізольовані — у них немає спільного React контексту. Для комунікації використовуйте:

Nano Stores (рекомендується для Astro):

// src/stores/cart.ts
import { atom, computed } from 'nanostores';
import { persistentAtom } from '@nanostores/persistent';

export const cartItems = persistentAtom<CartItem[]>('cart', [], {
  encode: JSON.stringify,
  decode: JSON.parse,
});

export const cartCount = computed(cartItems, items => items.length);
export const cartTotal = computed(cartItems, items =>
  items.reduce((sum, item) => sum + item.price * item.qty, 0)
);

export function addToCart(product: Product) {
  const items = cartItems.get();
  const existing = items.find(i => i.id === product.id);
  if (existing) {
    cartItems.set(items.map(i => i.id === product.id ? { ...i, qty: i.qty + 1 } : i));
  } else {
    cartItems.set([...items, { ...product, qty: 1 }]);
  }
}
// islands/CartIcon.tsx — React остров
import { useStore } from '@nanostores/react';
import { cartCount } from '@/stores/cart';

export function CartIcon() {
  const count = useStore(cartCount);
  return (
    <a href="/cart" className="relative">
      <ShoppingCartIcon />
      {count > 0 && <span className="badge">{count}</span>}
    </a>
  );
}
<!-- islands/AddToCartButton.svelte — Svelte остров -->
<script>
  import { addToCart } from '@/stores/cart';
  export let product;

  let loading = false;

  async function handleAdd() {
    loading = true;
    addToCart(product);
    loading = false;
  }
</script>

<button on:click={handleAdd} disabled={loading}>
  {loading ? 'Додаємо...' : 'У корзину'}
</button>

Обидва острови (React та Svelte) працюють з одним сховищем. Змінення в одному — негайно відображається в іншому.

Нативні браузерні події:

// Універсальна шина подій через CustomEvent
export function emit<T>(event: string, detail: T) {
  window.dispatchEvent(new CustomEvent(event, { detail, bubbles: true }));
}

export function on<T>(event: string, handler: (detail: T) => void) {
  const wrapped = (e: CustomEvent<T>) => handler(e.detail);
  window.addEventListener(event, wrapped as EventListener);
  return () => window.removeEventListener(event, wrapped as EventListener);
}

// В острові 1
emit('product:added', { id: product.id, name: product.name });

// В острові 2
useEffect(() => {
  return on<{ id: string; name: string }>('product:added', ({ name }) => {
    showToast(`${name} додано у корзину`);
  });
}, []);

Рендеринг островів на кількох фреймворках

Astro підтримує кілька рендерер-плагінів одночасно:

// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import vue from '@astrojs/vue';
import svelte from '@astrojs/svelte';
import preact from '@astrojs/preact';

export default defineConfig({
  integrations: [
    react(),           // Для існуючих React-компонентів
    vue(),             // Для Vue-компонентів з бібліотеки
    svelte(),          // Для легких інтерактивних віджетів
    preact({ compat: true }), // Preact як замінник React для легких островів
  ],
});

На практиці: різні фреймворки для різних цілей в межах одного сайту без конфліктів.

Продуктивність: цифри

Вимірювання на реальних проектах після міграції на Islands Architecture:

Метрика До (Full React SSR) Після (Astro Islands)
Initial JS 180–250 KB 15–40 KB
TTI 3.2s 0.8s
TBT 480ms 60ms
Lighthouse Performance 62 96

Розброс залежить від кількості та складності островів.

Обмеження паттерну

Islands Architecture не підходить для:

  • SPA-застосунків — потрібно єдине стан між багатьма компонентами
  • Дашбордів — занадто багато інтерактивності, острови теряють ізоляцію
  • Сторінок, де все інтерактивно — мінімальна користь

Ідеальний випадок: контентний сайт з кількома інтерактивними блоками на сторінці.

Строки реалізації

  • Тиждень 1–2: аудит існуючого сайту, виявлення інтерактивних компонентів, налаштування Astro + рендерер-плагіни
  • Тиждень 3: перенос статичних сторінок, розбивка на острови з директивами гідрації
  • Тиждень 4: комунікація між островами через nano stores, тестування ізоляції
  • Тиждень 5: вимірювання Core Web Vitals, порівняння з baseline, оптимізація директив
  • Тиждень 6: розгортання (Cloudflare Pages / Netlify), Lighthouse CI, фінальна документація