Fresh — современный фреймворк для разработки веб-приложений на JavaScript и TypeScript, ориентированный на скорость, простоту и использование возможностей серверного рендеринга. Одной из ключевых особенностей является работа с серверными компонентами (Server Components), где обработка данных происходит на сервере до отправки HTML на клиент.
Серверные компоненты в Fresh выполняются на сервере и сразу возвращают готовый HTML. Основные преимущества такого подхода:
Загрузка данных в серверных компонентах Fresh строится вокруг функции
handler и специальных методов fetch, а также
встроенной поддержки асинхронного кода. Пример базовой структуры:
import { Handlers, PageProps } from "$fresh/server.ts";
export const handler: Handlers = {
async GET(_req, ctx) {
const data = await fetch("https://api.example.com/items")
.then(res => res.json());
return ctx.render({ items: data });
},
};
export default function Page({ data }: PageProps) {
return (
<div>
{data.items.map(item => (
<p key={item.id}>{item.name}</p>
))}
</div>
);
}
Ключевые моменты:
handler отвечает за обработку HTTP-запроса.ctx.render передает данные в компонент для
рендеринга.В Fresh серверные компоненты могут использовать
async/await для загрузки данных из внешних API, баз данных
или локальных файлов:
export default async function Users() {
const response = await fetch("https://api.example.com/users");
const users = await response.json();
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.username}</li>
))}
</ul>
);
}
В этом примере HTML формируется только после завершения запроса к API, что гарантирует, что клиент увидит полный контент сразу.
Для серверных компонентов характерна интеграция с базами данных
напрямую на сервере. Например, используя Deno и
PostgreSQL:
import { Client } from "https://deno.land/x/postgres/mod.ts";
export default async function Posts() {
const client = new Client({ user: "user", database: "blog", hostname: "localhost", password: "pass" });
await client.connect();
const result = await client.queryArray("SELECT id, title FROM posts");
await client.end();
return (
<div>
{result.rows.map(([id, title]) => (
<h2 key={id}>{title}</h2>
))}
</div>
);
}
Особенности:
Для увеличения производительности важно использовать кэширование данных. Fresh позволяет комбинировать серверные компоненты с заголовками кэширования HTTP:
export const handler: Handlers = {
async GET(_req, ctx) {
const data = await fetch("https://api.example.com/data").then(res => res.json());
const response = ctx.render({ data });
response.headers.set("Cache-Control", "s-maxage=60, stale-while-revalidate=30");
return response;
},
};
Пояснение:
s-maxage указывает, сколько времени кэшировать на
сервере.stale-while-revalidate позволяет отдавать устаревший
контент, пока обновляется новый.Хотя серверные компоненты отвечают за загрузку данных и рендеринг HTML, их можно сочетать с клиентскими компонентами для интерактивности:
import Counter from "../islands/Counter.tsx";
export default async function Dashboard() {
const stats = await fetch("https://api.example.com/stats").then(res => res.json());
return (
<div>
<h1>Статистика</h1>
<p>Пользователи: {stats.users}</p>
<Counter />
</div>
);
}
Заметки:
Серверные компоненты позволяют обрабатывать ошибки до передачи данных клиенту:
export const handler: Handlers = {
async GET(_req, ctx) {
try {
const data = await fetch("https://api.example.com/items").then(res => res.json());
return ctx.render({ items: data });
} catch (err) {
return ctx.render({ error: "Не удалось загрузить данные" });
}
},
};
export default function Page({ data }) {
if (data.error) return <p>{data.error}</p>;
return <ul>{data.items.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
}
Преимущество:
handler.try/catch и заголовки Cache-Control.ctx.render или возвращаемый результат асинхронной
функции.Загрузка данных в серверных компонентах Fresh обеспечивает высокую производительность, безопасность и минимизацию объема кода на клиенте, делая приложения быстрыми и отзывчивыми даже при сложной структуре данных.