Миграция с Next.js на Fresh требует понимания архитектурных различий между обоими фреймворками. Next.js ориентирован на универсальный рендеринг (SSR и SSG) с сильной интеграцией React, тогда как Fresh использует нативные веб-компоненты и Islands Architecture, минимизируя JavaScript на клиенте и ориентируясь на Deno. Это накладывает определённые ограничения и открывает возможности для оптимизации производительности.
В Next.js проект имеет стандартную структуру:
/pages
/components
/public
/styles
В Fresh структура проекта отличается:
/routes
/components
/islands
/static
/routes/index.tsx → /).Понимание этой структуры позволяет правильно распределять код между сервером и клиентом.
Next.js использует файловую маршрутизацию в pages и
динамические маршруты через [param].tsx. Fresh применяет
похожий подход:
// routes/posts/[id].tsx
import { Handlers, PageProps } from "$fresh/server.ts";
export const handler: Handlers = {
async GET(req, ctx) {
const { id } = ctx.params;
const post = await fetchPostById(id);
return ctx.render(post);
},
};
export default function PostPage({ data }: PageProps<Post>) {
return <div>{data.title}</div>;
}
Особенности:
ctx содержит параметры маршрута и метод
render() для передачи данных в компонент.Next.js использует getServerSideProps,
getStaticProps и getStaticPaths для SSR/SSG. В
Fresh эквивалент реализуется через Handlers:
export const handler: Handlers = {
async GET(req, ctx) {
const data = await fetchData();
return ctx.render(data);
},
};
ctx.render(data) передаёт данные в компонент
страницы.ctx.params.export const prerender = true; в маршруте, что позволяет
заранее рендерить страницы на этапе сборки.Next.js: большинство компонентов React, интерактивные компоненты рендерятся как обычные React-компоненты.
Fresh: компоненты делятся на серверные и клиентские (Islands):
// components/PostList.tsx - серверный компонент
export default function PostList({ posts }: { posts: Post[] }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
);
}
// islands/LikeButton.tsx - клиентский компонент
import { useState } from "preact/hooks";
export default function LikeButton() {
const [count, setCount] = useState(0);
return <button onCl ick={() => setCount(count + 1)}>Like {count}</button>;
}
Особенности миграции:
Next.js поддерживает CSS Modules, styled-jsx, Tailwind и другие подходы. Fresh работает с любым CSS, но нет встроенной поддержки CSS-in-JS:
// routes/index.tsx
import "../static/styles.css";
export default function Home() {
return <h1 class="title">Привет, Fresh!</h1>;
}
Для Tailwind настройка аналогична Deno: подключение через PostCSS и build-скрипт.
Next.js: API routes через pages/api. Fresh: API
создаются как серверные маршруты без отдельного каталога:
// routes/api/posts.ts
import { Handlers } from "$fresh/server.ts";
export const handler: Handlers = {
async GET() {
const posts = await fetchPosts();
return new Response(JSON.stringify(posts), {
headers: { "Content-Type": "application/json" },
});
},
};
Особенности:
Next.js часто использует Redux, Context API или SWR. В Fresh:
useEffect
и fetch внутри Islands.https://deno.land/...).ctx.params.export const prerender = true для страниц,
которые можно заранее рендерить.Fresh позволяет существенно оптимизировать загрузку и производительность за счёт уменьшения клиентского JavaScript. Правильное разделение компонентов и маршрутов является ключевым этапом при миграции с Next.js.