Формы в островах

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


Серверный рендеринг и формы

В Fresh формы могут существовать в основном на сервере. HTML-форма создаётся с использованием стандартного <form> и управляется серверными обработчиками (handlers) через API роуты. Такой подход обеспечивает:

  • Минимальную загрузку JavaScript Большинство форм остаются статичными до момента взаимодействия пользователя.

  • Повышенную безопасность Обработка данных происходит на сервере, что снижает риск уязвимостей XSS и CSRF.

Пример простой формы регистрации на сервере:

/** @jsx h */
import { h } from "preact";

export default function SignupForm() {
  return (
    <form method="POST" action="/signup">
      <label>
        Имя:
        <input type="text" name="name" required />
      </label>
      <label>
        Email:
        <input type="email" name="email" required />
      </label>
      <button type="submit">Зарегистрироваться</button>
    </form>
  );
}

На сервере обрабатывается POST-запрос через роут /signup, который получает данные, валидирует их и сохраняет.


Острова интерактивности для форм

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

Пример использования интерактивного острова:

/** @jsx h */
import { h } from "preact";
import { useState } from "preact/hooks";

export default function InteractiveForm() {
  const [email, setEmail] = useState("");
  const [error, setError] = useState("");

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!email.includes("@")) {
      setError("Некорректный email");
      return;
    }
    setError("");
    const response = await fetch("/subscribe", {
      method: "POST",
      body: JSON.stringify({ email }),
      headers: { "Content-Type": "application/json" },
    });
    if (!response.ok) setError("Ошибка сервера");
  };

  return (
    <form onSub mit={handleSubmit}>
      <input
        type="email"
        value={email}
        onIn put={(e) => setEmail(e.target.value)}
        placeholder="Email"
      />
      <button type="submit">Подписаться</button>
      {error && <p>{error}</p>}
    </form>
  );
}

Ключевые особенности интерактивного острова:

  • Локальное состояние формы управляется хуками Preact.
  • Обработка событий происходит на клиенте без полной перезагрузки страницы.
  • Загрузка скрипта только при необходимости, что ускоряет первоначальный рендеринг.

Интеграция серверных и клиентских форм

Важным аспектом является совместная работа серверной и клиентской логики. Fresh позволяет:

  1. Начальный рендеринг на сервере — форма доступна сразу с HTML.
  2. Подключение интерактивного острова — только для компонентов, требующих динамики.

Паттерн «сервер + остров» гарантирует:

  • Быстрый Time-to-Interactive (TTI) для страницы.
  • Минимальный JavaScript на основном документе.
  • Возможность масштабирования больших приложений с множеством форм.

Валидация и обработка ошибок

Ошибки формы могут быть обработаны:

  • На сервере — для базовой проверки и безопасности.
  • На клиенте — для улучшенного UX, интерактивных подсказок и мгновенной обратной связи.

Пример комбинированной валидации:

// Серверная часть
export async function POST(req) {
  const data = await req.json();
  if (!data.email.includes("@")) {
    return Response.json({ error: "Неверный email" }, { status: 400 });
  }
  // Сохранение данных
  return Response.json({ success: true });
}
// Клиентский остров
const handleSubmit = async (e) => {
  e.preventDefault();
  const response = await fetch("/subscribe", {
    method: "POST",
    body: JSON.stringify({ email }),
    headers: { "Content-Type": "application/json" },
  });
  const result = await response.json();
  if (result.error) setError(result.error);
};

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


Поддержка динамических полей

Острова позволяют добавлять или удалять поля формы на лету, изменять состояния в зависимости от ввода пользователя:

const [fields, setFields] = useState([{ name: "" }]);
const addField = () => setFields([...fields, { name: "" }]);

HTML рендерится через map, а новые поля управляются состоянием острова. Такой подход недоступен для чисто серверных форм без JavaScript.


Асинхронная отправка и UX

Использование островов улучшает UX:

  • Асинхронная отправка формы через fetch предотвращает перезагрузку страницы.
  • Интерактивные сообщения об ошибках и успехе обновляются мгновенно.
  • Снижение нагрузки на сервер — только данные формы передаются, без повторного рендера всей страницы.

Рекомендации по организации форм в Fresh

  • Выделять интерактивные элементы в отдельные острова.
  • Оставлять базовые поля доступными для серверного рендера.
  • Использовать хуки для управления состоянием и валидацией.
  • Поддерживать гибридную архитектуру: серверная проверка + клиентская интерактивность.
  • Минимизировать количество JavaScript на странице, оставляя интерактивность только там, где она необходима.

Фреймворк Fresh с островами интерактивности позволяет создавать быстрые, безопасные и удобные формы, сочетая преимущества серверного рендера и локальной клиентской логики.