Сравнение загрузки страниц в 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 может повторить этот подход, обеспечивая гибкость для серверной и клиентской логики.

Оглавление

Что такое загрузка страниц в веб-фреймворках

Загрузка страниц — это процесс получения данных из 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) хранят состояние.
  • Отсутствие useEffect Server 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)
Гибкость Фокус на серверо-ориентированности Высокая гибкость (серверные и клиентские опции) Гибкий, но требует больше ручного управления

Основные моменты

  1. Использование Server Components в Next.js Загружайте данные в app/feed/page.tsx и используйте <Link> для навигации.
  2. Хранение состояния в URL Параметры запроса (?tag=react) упрощают управление состоянием.
  3. Оптимизация производительности В Remix используется defer, в Next.js — Suspense и кэширование fetch.

Заключение

Remix упрощает загрузку страниц с помощью loader и состояния в URL, исключая useState и useEffect. В Next.js можно воспроизвести этот подход через Server Components . Оба фреймворка минимизируют клиентскую сложность, делая их подходящими для современных веб-приложений.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

*