Fresh — это современный подход к построению веб-приложений, ориентированный на максимально простую и эффективную синхронизацию состояния между сервером и клиентом. Основная идея заключается в минимизации промежуточных слоёв, таких как клиентские фреймворки или избыточная логика рендеринга, и использовании серверного рендеринга с динамической актуализацией данных на клиенте.
Fresh применяется преимущественно в экосистеме Deno, но концептуально его принципы можно переносить на любые JavaScript-приложения. Отличие Fresh от классических SPA заключается в том, что весь рендеринг страниц происходит на сервере, а клиент получает уже готовый HTML с встроёнными интерактивными элементами.
Архитектура строится вокруг трёх ключевых компонентов:
Роутинг на сервере Каждый URL напрямую соответствует серверной функции или файлу. Сервер обрабатывает запрос, формирует HTML и возвращает его клиенту. Такой подход исключает необходимость отдельного маршрутизатора на клиенте и упрощает управление состоянием.
Серверный рендеринг (SSR) Страницы полностью рендерятся на сервере, включая все данные из базы или API. Клиент получает уже готовую структуру DOM, что ускоряет первый рендер и улучшает SEO.
Интерактивные компоненты (Islands) В Fresh используется концепция «островов» — отдельных интерактивных компонентов, которые загружаются и инициализируются на клиенте. Всё остальное остаётся статическим HTML, что минимизирует нагрузку на браузер.
В Fresh состояние разделяется на серверное и клиентское.
Серверное состояние хранится в базе данных или в памяти сервера. Оно формирует исходный HTML. Любые изменения, инициированные сервером, автоматически отражаются при следующем рендере страницы.
Клиентское состояние ограничено интерактивными островами. Компоненты получают данные через свойства (props) и могут инициировать действия на сервер, например, через fetch-запросы. После обновления сервер возвращает новые данные, которые обновляют компонент без перезагрузки всей страницы.
Пример синхронизации:
// server.ts
export const handler = async (req: Request) => {
const data = await getDataFromDB();
return new Response(renderPage({ data }));
};
// island.ts
export default function Counter({ initialCount }) {
let count = initialCount;
function increment() {
fetch("/increment").then(res => res.json()).then(updated => {
count = updated.count;
render();
});
}
return html`<button onCl ick=${increment}>${count}</button>`;
}
Здесь сервер формирует исходное состояние, а интерактивный остров обновляет его динамически.
1. Pull-подход Клиент периодически запрашивает состояние с сервера. Этот способ прост, но может приводить к лишним запросам.
2. Push-подход Сервер отправляет обновления на клиент через WebSocket или SSE (Server-Sent Events). Эффективно для приложений с высокой динамикой, например, чатов или торговых платформ.
3. Комбинированный подход Чаще всего используется гибрид: критическое состояние формируется сервером при рендере страницы, а динамические обновления передаются через push-механизмы.
Fresh использует стратегию edge-first rendering. Страницы рендерятся на сервере, а затем кешируются на уровне CDN или прокси. Это уменьшает нагрузку на сервер и ускоряет доставку контента.
| Характеристика | Fresh | SPA (React/Vue) |
|---|---|---|
| Рендеринг | Сервер + Islands | Клиент |
| Первый рендер | Быстрый, готовый HTML | Медленнее, зависит от JS |
| Управление состоянием | Серверное + локальное на островах | Клиентское, часто сложные менеджеры |
| SEO | Естественный, HTML готов к индексации | Необходим SSR или pre-render |
| Загрузка JS | Минимальная, только интерактивные компоненты | Полная загрузка всего фреймворка |
Fresh особенно эффективен в приложениях:
Интеграция с существующими API и базами данных происходит напрямую на сервере. Клиент получает лишь данные для интерактивных островов, что делает приложение лёгким и масштабируемым.