Fresh — это веб-фреймворк для Deno, в основе которого лежит строгий и последовательно реализованный принцип: HTML генерируется на сервере всегда, если нет явной необходимости делать иначе. В отличие от большинства современных JavaScript-фреймворков, где серверный рендеринг является опцией или надстройкой, в Fresh он выступает базовым режимом работы.
Этот подход влияет на всю архитектуру приложения: маршрутизацию, загрузку JavaScript, работу с состоянием и производительность.
При каждом HTTP-запросе сервер Fresh:
Браузер получает документ, который уже содержит весь необходимый HTML для отображения страницы. JavaScript на клиенте не требуется для первичного отображения.
Это принципиально отличает Fresh от SPA-подхода, где браузер сначала загружает JavaScript-бандл, а затем строит интерфейс.
Fresh использует Preact для описания UI, но рендеринг компонентов происходит на сервере:
export default function Home() {
return <h1>Hello Fresh</h1>;
}
Этот компонент:
На клиент отправляется уже отрендеренный
<h1>Hello Fresh</h1>, а не описание компонента
или виртуальное дерево.
Ключевая особенность Fresh — нулевой JavaScript на клиенте, если явно не указано обратное.
HTML работает сам по себе: ссылки, формы, навигация через обычные HTTP-запросы.
Это означает:
Fresh допускает клиентскую интерактивность, но строго локализованную. Для этого используется концепция islands.
Island — это компонент, который:
Пример:
// islands/Counter.tsx
import { useState } from "preact/hooks";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onCl ick={() => setCount(count + 1)}>
{count}
</button>
);
}
Использование в серверном компоненте:
import Counter from "../islands/Counter.tsx";
export default function Page() {
return <Counter />;
}
Все остальные части страницы остаются чистым HTML без JavaScript.
Маршруты в Fresh соответствуют файловой структуре:
routes/
├─ index.tsx
├─ blog/
│ └─ [slug].tsx
Каждый маршрут — это серверный модуль, который может:
Пример серверного маршрута:
import { Handlers } from "$fresh/server.ts";
export const handler: Handlers = {
async GET(req, ctx) {
const data = await fetchData();
return ctx.render(data);
},
};
export default function Page(props) {
return <pre>{JSON.stringify(props.data)}</pre>;
}
Данные загружаются на сервере, а HTML формируется сразу с ними.
В SPA и гибридных фреймворках часто возникает ситуация:
Fresh избегает этого:
Это снижает количество ошибок, связанных с различиями окружений, и упрощает отладку.
Серверный рендеринг по умолчанию даёт измеримые преимущества:
Fresh хорошо масштабируется на edge-платформах благодаря короткому времени жизни запросов и отсутствию тяжёлых клиентских бандлов.
Так как HTML формируется на сервере:
Семантическая разметка работает без компромиссов, а доступность не зависит от состояния клиентского JavaScript.
SSR по умолчанию позволяет использовать классические веб-механизмы:
<form method="post">
<input name="email" />
<button>Send</button>
</form>
POST-запрос обрабатывается сервером, страница перерендеривается и возвращается клиенту. Это:
Навигация по <a href> также приводит к
полноценному серверному рендерингу следующей страницы.
| Характеристика | Fresh | Типичный SPA |
|---|---|---|
| Рендеринг | Серверный | Клиентский |
| JS по умолчанию | Нет | Да |
| Гидратация | Только islands | Вся страница |
| Бандлы | Нет глобальных | Крупные |
| SEO | Из коробки | Через SSR |
Fresh возвращает разработку к классической модели веба, добавляя интерактивность точечно, а не повсеместно.
Серверный рендеринг по умолчанию в Fresh — не оптимизация, а фундамент. Он:
Fresh не пытается быть универсальным фреймворком для всего. Он сознательно выбирает сервер как основной источник истины и строит вокруг этого все остальные механизмы.