Модель данных и состояние

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


Архитектура и принципы Fresh

Fresh построен на принципах Islands Architecture, где основная страница рендерится на сервере, а интерактивные элементы (islands) подключаются по мере необходимости. Такая модель позволяет:

  • Сократить объём JavaScript на клиенте.
  • Ускорить время первого отображения страницы.
  • Легко интегрировать реактивные компоненты, не теряя серверного рендеринга.

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


Работа с состоянием

Состояние в Fresh делится на три уровня:

1. Серверное состояние

Серверное состояние формируется на этапе рендеринга страницы. Для его получения используются функции типа handler:

export const handler = {
  GET(req, ctx) {
    const data = fetchDataFromDB();
    return ctx.render({ data });
  },
};
  • ctx.render() передаёт данные компоненту для рендеринга.
  • Серверное состояние не доступно напрямую на клиенте, оно передаётся через HTML и JSON.

2. Компонентное состояние (Client Islands)

Интерактивные элементы используют локальное состояние через стандартные React-подобные подходы:

import { useState } from "preact/hooks";

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

  return (
    <button onCl ick={() => setCount(count + 1)}>
      {count}
    </button>
  );
}
  • Локальное состояние привязано к конкретной «island».
  • Обновление состояния происходит на клиенте, не затрагивая серверное рендеринг.

3. Глобальное состояние

Для управления общими данными рекомендуется использовать контексты:

import { createContext } from "preact";
import { useContext } from "preact/hooks";

const ThemeContext = createContext("light");

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState("light");
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

function ThemedButton() {
  const { theme, setTheme } = useContext(ThemeContext);
  return <button onCl ick={() => setTheme(theme === "light" ? "dark" : "light")}>{theme}</button>;
}
  • Контексты позволяют синхронизировать состояние между различными islands.
  • Поддержка реактивного обновления компонентов при изменении глобального состояния.

Доступ к данным и сериализация

Fresh передаёт серверные данные на клиент через props, автоматически сериализуемые в JSON. Для сложных типов (например, Date или Map) требуется ручная сериализация:

return ctx.render({ date: date.toISOString() });

На клиенте их можно десериализовать обратно:

const date = new Date(props.date);

Такой подход гарантирует безопасность данных и избежание утечек состояния между пользователями.


Реактивность и оптимизация

Fresh минимизирует клиентский JavaScript, поэтому интерактивные элементы должны быть максимально локализованными. Рекомендации:

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

Работа с формами и событиями

В Fresh формы и события часто обрабатываются на сервере, а клиентский код служит лишь для улучшения UX:

export const handler = {
  POST: async (req, ctx) => {
    const formData = await req.formData();
    const name = formData.get("name");
    saveToDB(name);
    return ctx.render({ message: `Saved ${name}` });
  },
};
  • Такой подход уменьшает зависимость от клиентского кода.
  • Состояние формы синхронизируется с сервером через рендеринг.

Если требуется мгновенная реакция на изменения, можно подключить island-компонент, который управляет локальным состоянием и обновляет DOM без полной перезагрузки страницы.


Итоговые особенности модели данных Fresh

  • Иммутабельность: props и состояние компонентов не изменяются напрямую.
  • Серверная авторитетность: основной источник данных — сервер, что повышает согласованность.
  • Локальная интерактивность: client islands управляют интерактивным поведением без перегрузки страницы.
  • Гибкая синхронизация: глобальные состояния через контексты обеспечивают согласованность между компонентами.
  • Минимизация JS на клиенте: улучшает производительность и ускоряет рендеринг.

Эта архитектура позволяет создавать веб-приложения с предсказуемым поведением, высокой производительностью и лёгким управлением состоянием, сочетая лучшие практики серверного рендеринга и реактивных интерфейсов.