Сравнение загрузки страниц в Remix и Next.js
Изучая документацию по FSD, я обнаружил пример по Remix демонстрирующий очень простой подход к загрузке страницы. И решил воспроизвести этот подход в Next.js.
Сравнение загрузки страниц в Remix и Next.js
Remix и Next.js — два SSR JavaScript-фреймворка, предлагающих разные подходы к загрузке страниц. Remix использует серверную логику и URL для управления состоянием, исключая необходимость в React-хуках useState и useEffect. Next.js с App Router и Server Components может повторить этот подход, обеспечивая гибкость для серверной и клиентской логики.
Оглавление
- Что такое загрузка страниц в веб-фреймворках
- Загрузка страниц в Remix
- Воспроизведение подхода Remix в Next.js
- Когда в Next.js появляется useEffect
- Таблица сравнения Remix против Next.js
- Основные моменты
- Заключение
Что такое загрузка страниц в веб-фреймворках
Загрузка страниц — это процесс получения данных из API или базы данных и их отображения на веб-странице. В традиционных React-приложениях используются useState для управления данными и useEffect для их загрузки при монтировании компонента или изменении параметров, что может усложнять код из-за управления побочными эффектами и синхронизации состояния.
Remix упрощает процесс, перенося загрузку данных на сервер и используя параметры URL как источник состояния, что устраняет необходимость в клиентских хуках. Next.js с App Router и Server Components также поддерживает этот подход, но позволяет использовать и клиентскую загрузку данных, где хуки могут быть нужны.
Загрузка страниц в Remix
Ключевые особенности
- Серверные функции
loaderКаждый маршрут может экспортировать функциюloader, выполняющуюся на сервере для получения данных. Она автоматически вызывается при доступе к маршруту или изменении URL. - URL как состояние Параметры запроса (например,
?tag=react) хранят состояние, устраняя необходимость вuseState. - Отсутствие
useEffectЗагрузка данных происходит на сервере, что исключает необходимость в клиентских побочных эффектах.
Пример Фильтрация статей по тегам
Рассмотрим пример фильтрации статей по тегам (например, “react” или “javascript”) с использованием RealWorld API, без useState и useEffect.
Серверная функция loader
Функция loader получает статьи и теги, фильтруя по параметру tag.
// pages/feed/api/loader.ts
import { json, type LoaderFunctionArgs } from "@remix-run/node";
import { promiseHash } from "remix-utils/promise";
import { GET } from "shared/api";
async function throwAnyErrors(
responsePromise: Promise>,
) {
const { data, error, response } = await responsePromise;
if (error !== undefined) {
throw json(error, { status: response.status });
}
return data as NonNullable;
}
export const loader = async ({ request }: LoaderFunctionArgs) => {
const url = new URL(request.url);
const selectedTag = url.searchParams.get("tag") ?? undefined;
return json(
await promiseHash({
articles: throwAnyErrors(
GET("/articles", { params: { query: { tag: selectedTag } } })
),
tags: throwAnyErrors(GET("/tags")),
})
);
};
Код читает параметр tag из URL, запрашивает отфильтрованные статьи и теги, возвращая данные в JSON.
Клиентский компонент
Компонент FeedPage отображает статьи и теги, используя форму для обновления URL.
Компонент использует useLoaderData для доступа к данным и рендерит статьи и теги в виде кнопок в форме <Form>.
Конфигурация маршрута
Маршрут связывает loader и компонент FeedPage для маршрута /.
Почему нет useState или useEffect
- Нет
useStateТег хранится в URL (?tag=react). - Нет
useEffectЗагрузка данных происходит вloaderна сервере.
Воспроизведение подхода Remix в Next.js
Ключевые особенности
- Server Components Выполняются на сервере, загружая данные и рендеря HTML без клиентского JavaScript.
- URL как состояние Параметры запроса (
?tag=react) хранят состояние. - Отсутствие
useEffectServer Components загружают данные во время рендеринга. - Мягкая навигация Поддерживается через
<Link>, обновляя URL без перезагрузки.
Пример Фильтрация статей по тегам
Серверная загрузка данных
// lib/api.ts
export async function getArticles(tag?: string) {
const response = await fetch(
`https://api.realworld.io/api/articles${tag ? `?tag=${tag}` : ""}`,
{ cache: "force-cache" }
);
if (!response.ok) throw new Error("Не удалось загрузить статьи");
return response.json();
}
export async function getTags() {
const response = await fetch("https://api.realworld.io/api/tags", {
cache: "force-cache",
});
if (!response.ok) throw new Error("Не удалось загрузить теги");
return response.json();
}
Функции запрашивают статьи и теги, используя кэширование для оптимизации.
Server Component
Компонент загружает данные на сервере и рендерит интерфейс с тегами в виде <Link>.
Компонент ArticlePreview
Почему нет useState или useEffect
- Нет
useStateТег хранится в URL. - Нет
useEffectДанные загружаются на сервере.
Улучшение с помощью Server Actions
Для имитации поведения <Form> в Remix можно использовать Server Actions.
Server Action перенаправляет на новый URL, вызывая перерендеринг без useEffect.
Когда в Next.js появляется useEffect
В Client Components ("use client") useEffect нужен для динамической загрузки данных.
useEffect отслеживает изменения searchParams и загружает данные. Server Components позволяют избежать этого.
Таблица сравнения Remix против Next.js
Сравнение удобства загрузки страниц в Remix и Next.js.
| Аспект | Remix | Next.js (Server Components) | Next.js (Client Component) |
|---|---|---|---|
Устранение useEffect |
Да, через loader и useLoaderData |
Да, через Server Components | Нет, требуется useEffect для загрузки данных |
Устранение useState |
Да, состояние в URL | Да, состояние в URL | Да, состояние в URL, но useState для данных |
| Навигация | Мягкая (SPA), loader вызывается автоматически |
Мягкая (SPA), Server Component перерендеривается | Мягкая (SPA), useEffect для повторных запросов |
| Интеграция форм | Декларативная (<Form> вызывает loader/action) |
Требует Server Actions и redirect |
Требует useEffect или клиентской логики |
| Кэширование | Встроенное, через Cache-Control |
Автоматическое, через fetch/revalidate |
Ручное (через fetch или состояние) |
| Поведение без JS | Работает (формы отправляются как HTML) | Ограниченно (формы требуют Server Actions) | Не работает (требуется JS для useEffect) |
| Гибкость | Фокус на серверо-ориентированности | Высокая гибкость (серверные и клиентские опции) | Гибкий, но требует больше ручного управления |
Основные моменты
- Использование Server Components в Next.js Загружайте данные в
app/feed/page.tsxи используйте<Link>для навигации. - Хранение состояния в URL Параметры запроса (
?tag=react) упрощают управление состоянием. - Оптимизация производительности В Remix используется
defer, в Next.js —Suspenseи кэшированиеfetch.
Заключение
Remix упрощает загрузку страниц с помощью loader и состояния в URL, исключая useState и useEffect. В Next.js можно воспроизвести этот подход через Server Components . Оба фреймворка минимизируют клиентскую сложность, делая их подходящими для современных веб-приложений.