Создание островов

Fresh — современный фреймворк для разработки веб-приложений на базе JavaScript и Deno, ориентированный на создание быстрых, лёгких и оптимизированных страниц. Одной из ключевых особенностей Fresh является концепция островов (islands), позволяющая сочетать статическую генерацию и интерактивные элементы без потери производительности.


Концепция островов

Остров — это автономный интерактивный компонент, который может содержать JavaScript, управлять состоянием и взаимодействовать с пользователем. В отличие от традиционных SPA, где весь интерфейс рендерится на клиенте, Fresh использует гибридный подход:

  • Основная страница генерируется статически на сервере.
  • Интерактивные части (острова) загружаются динамически и инициализируются на клиенте.

Преимущества островов:

  • Минимизация объёма JavaScript, загружаемого на клиент.
  • Быстрая первичная загрузка страницы.
  • Чёткое разделение статического контента и интерактивной логики.
  • Возможность использования привычных React-подобных компонентов.

Создание острова

Для создания острова используется директива export default в компоненте, а подключение происходит через специальный хелпер Island.

Пример структуры острова:

// components/Counter.tsx
/** @jsx h */
import { h } from "preact";
import { useState } from "preact/hooks";

export default function Counter() {
  const [count, setCount] = useState(0);

  return (
    <button onCl ick={() => setCount(count + 1)}>
      Нажато {count} раз
    </button>
  );
}

Подключение острова в шаблоне страницы:

// routes/index.tsx
/** @jsx h */
import { h } from "preact";
import Counter from "../components/Counter.tsx";
import { Island } from "$fresh/runtime.ts";

export default function Home() {
  return (
    <div>
      <h1>Главная страница</h1>
      <Island component={Counter} />
    </div>
  );
}

Ключевые моменты:

  • Компонент Counter полностью автономен и может содержать любое состояние.
  • <Island component={Counter} /> указывает Fresh, что этот компонент нужно инициализировать на клиенте после загрузки HTML.
  • Всё остальное остаётся статическим, что ускоряет рендеринг.

Управление состоянием и эффектами

Острова в Fresh используют хуки Preact, такие как useState и useEffect. Поскольку остров изолирован, его состояние не влияет на другие части страницы, если не передавать данные явно через пропсы или контекст.

Пример с эффектами:

import { h } from "preact";
import { useEffect, useState } from "preact/hooks";

export default function Clock() {
  const [time, setTime] = useState(new Date().toLocaleTimeString());

  useEffect(() => {
    const interval = setInterval(() => {
      setTime(new Date().toLocaleTimeString());
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return <p>Текущее время: {time}</p>;
}

Здесь компонент Clock обновляет своё состояние каждую секунду, но остальная страница остаётся полностью статической.


Ленивая загрузка и оптимизация

Fresh позволяет загружать острова лениво, чтобы уменьшить нагрузку на клиент:

<Island component={Counter} defer />
  • defer — атрибут, который откладывает инициализацию до полной загрузки HTML.
  • Такой подход полезен для компонентов, которые не критичны при первой загрузке страницы (например, виджеты, кнопки социальных сетей, графики).

Передача данных в острова

Остров может принимать пропсы с сервера, что позволяет строить динамические интерфейсы без лишних запросов:

// routes/index.tsx
import Greeting from "../components/Greeting.tsx";
import { Island } from "$fresh/runtime.ts";

export default function Home() {
  return (
    <div>
      <Island component={Greeting} name="Алексей" />
    </div>
  );
}

// components/Greeting.tsx
export default function Greeting({ name }: { name: string }) {
  return <p>Привет, {name}!</p>;
}

Таким образом, данные передаются сразу при рендере страницы, без дополнительного API-запроса.


Ограничения и рекомендации

  • Размер островов: Компоненты должны быть небольшими, иначе преимущества Fresh теряются.
  • Коммуникация между островами: Не существует глобального состояния; для обмена данными между островами необходимо использовать серверные запросы или хранилища типа localStorage.
  • SEO и доступность: Так как основной HTML генерируется на сервере, текст и ссылки остаются видимыми для поисковых систем, что улучшает SEO.

Архитектурные выводы

Использование островов позволяет строить:

  • Гибридные страницы: статический HTML + интерактивные элементы.
  • Модульные интерфейсы: каждый остров — автономный компонент с собственным состоянием и логикой.
  • Оптимизированный Jav * aScript: минимальная загрузка кода на клиенте.

Комбинация статической генерации и динамических островов делает Fresh подходящим для сложных веб-приложений, где критична производительность и скорость загрузки.