Remix vs Next.js — какой из них использовать?

Сегодня у нас есть множество различных вариантов фреймворков, когда мы хотим создать новый веб-проект на основе React. Будучи разработчиком, вы можете столкнуться с трудностями, пытаясь понять, какой из них выбрать или какой из них лучше всего подойдет для ваших нужд.

Один из самых распространенных фреймворков, который вы можете знать, — Next.js, широко используемый такими компаниями, как Netflix, Twitch или Uber. Он считается одним из самых быстроразвивающихся React-фреймворков.

Другим было трудно конкурировать с Next.js, поскольку он охватывает три различные стратегии рендеринга страниц, но с ноября 2021 года, похоже, у нас появился новый, свежий и мощный фреймворк под названием Remix.

Прости, Гэтсби, я не забыл тебя, и мне нравится, как ты работаешь при генерации статических сайтов.

Почему вам нужен Next.js или Remix вместо обычного React?

Может быть, и нет, это зависит от ситуации. Суть в том, чтобы понять, как работают приложения React, какие проблемы вы можете найти и как фреймворки, такие как Next.js или Remix, работают над их решением.

Суть React в том, что вы можете создавать одностраничные приложения (SPA), где для рендеринга всех веб-страниц используется только один HTML-файл, а маршрутизация остается на стороне клиента. Но что именно это значит?

  • При первом запросе браузер получает страницу HTML-бокса, которая содержит все приложения сразу.
  • Никакого предварительно отрендеренного контента
  • Когда срабатывает навигация пользователя, JavaScript заменяет только те компоненты и содержимое, которые связаны с запрошенным маршрутом, но HTML-файл остается прежним.

Короче говоря, браузер сам управляет тем, какой файл JavaScript должен быть загружен для рендеринга текущей страницы. Другими словами, Client-Side Rendering (CSR).

CSR может быть очень причудливым и полезным для создания приложений, которым не нужно заботиться о SEO, кэшировании или медленном начальном рендеринге.

  • SSlow Initial Render — Когда пользователь впервые заходит на сайт, загрузка первой содержательной страницы может занять много времени. Это означает, что нужно дать им возможность получить пустую страницу, пока JavaScript не загрузится и все не будет отображено.
  • SEO — поскольку контент будет состоять только из одной HTML-страницы, довольно сложно добиться его индексации поисковыми системами и ботами социальных сетей.
  • Проблемы с кэшированием — HTML-структура не может быть кэширована, поскольку она недоступна при первом рендеринге.

На этом этапе вы можете подумать, что SPA — это дьявол, но подумайте обо всех тех внутренних приложениях, которые используют многие компании, или о прикладных продуктах, которые они продают.

Next.js или Remix появляются тогда, когда вы хотите воспользоваться преимуществами работы SPA без потери трех моментов, о которых я говорил ранее. Сильной стороной является то, что веб-сайт может быть отрисован на стороне сервера перед отправкой клиенту.

SSR vs SSG vs ISR

Я уже объяснил, что означает и как работает CSR, теперь настала моя очередь поговорить о других модных стратегиях рендеринга страниц.

Next.js является мощным вариантом, поскольку он поставляется с 3 различными вариантами из коробки, в то время как Remix полностью полагается на SSR (на данный момент). Но как именно работают эти стратегии?

Рендеринг на стороне сервера (SSR)

Когда страница запрашивается, она полностью отображается на сервере перед отправкой клиенту. Это отличный вариант для сайтов с большим количеством динамических данных и контента.

Генерация статических сайтов (SSG)

Эта стратегия генерирует все страницы по маршруту во время сборки. Только запрашиваемая страница отправляется клиенту при появлении запроса. Другими словами, создается типичный статический сайт.

Это может быть подходящим вариантом для тех статических веб-сайтов, на которых мало или совсем нет динамических данных и не так много страниц. Каждое изменение в данных будет вызывать регенерацию всех страниц.

Инкрементная статическая регенерация (ISR)

Эта концепция была предложена Next.js и является своего рода гибридом SSR и SSG.

Она похожа на SSG, но вы можете установить интервал времени, чтобы знать, когда ваши страницы должны быть обновлены. Подходит для статических сайтов с динамическим содержимым.

Сильной стороной Next.js является то, что он позволяет переключаться между этими тремя вариантами на каждой странице, так что каждая из них может следовать разным стратегиям.

Возможно, вы спросите себя: «Почему я должен заменить Next.js на Remix, если он работает только с SSR». Ответ довольно прост, в настоящее время приложения и веб-сайты имеют тенденцию иметь динамические данные, и существует мало примеров, которые бы подходили для сценария SSG или ISR.

Принимая это во внимание, Remix предоставляет API более низкого уровня. Например, он раскрывает объект Request, чтобы вы могли легко изменять заголовки перед рендерингом страницы, в то время как в Next.js для этого потребуется промежуточное ПО.

Маршрутизация

Оба фреймворка используют файловую систему маршрутизации для рендеринга страниц.

  • Каждая страница хранится в отдельном файле.
  • Каждая страница ассоциируется с маршрутом на основе имени файла.
  • Каждая страница, в конечном итоге, рендерит React Component.

Маршрутизация в Remix

Вы добавите каждый маршрут в /app/routes, и каждый файл или папка, созданные внутри него, будут рассматриваться как маршрут.

Также вы найдете файл /app/root.jsx, который является «корневым маршрутом» и представляет собой макет для всего приложения. Он содержит теги ,

и теги, а также другие связанные с Remix вещи.

Маршрутизация в Next.js

Вы будете добавлять каждый маршрут в /pages, и это работает точно так же, как и в Remix.

В этом случае у вас может быть или не быть pages/_app.js и pages/_document.js. Где App используется для инициализации страниц, а Document содержит теги , и .

Next.js использует их по умолчанию без необходимости дополнительной настройки. Но если вам нужно настроить их, вы можете создать эти два файла, переопределив таким образом файлы по умолчанию.

Индексные маршруты

Оба они используют одну и ту же систему для отображения маршрута контейнера.

Пример с Next.js:

Пример с Remix:

Вложенные маршруты

Remix построен поверх React Router и той же командой, поэтому вы можете представить, кто является победителем, когда дело доходит до вложенных маршрутов.

Идея заключается в том, чтобы сделать активный маршрут для монтажа макета, содержащего несколько вложенных маршрутов, так что каждый маршрут управляет своим собственным содержимым.

Если бы мне пришлось объяснять это словами React, я бы сказал вам, что каждый вложенный маршрут — это компонент, и в зависимости от пути URL они могут быть смонтированы и размонтированы.

Для этого в Remix используется компонент <Outlet /> React-router. Он используется в родительских маршрутах, чтобы указать им отрисовывать элементы дочерних маршрутов всякий раз, когда они срабатывают.

Помните, что все эти вложенные маршруты предварительно загружаются на сервер, поэтому почти каждое состояние загрузки может быть удалено. Разве это не похоже на смесь между SSR и SPA?

Next.js, с другой стороны, поддерживает вложенные маршруты, но они работают как отдельная страница. Если вы хотите добиться чего-то похожего на то, что делает Remix, вам, вероятно, следует настроить ваш _app.js.

Пример с обоими вариантами:

Динамические маршруты

Они работают путем рендеринга различного контента на основе параметра URL, который является динамическим, но при этом используется один файл маршрута.

В данном случае оба фреймворка поддерживают эту функциональность, но используют разные соглашения об именовании.

Пример с Next.js

У него есть собственный хук useRouter, который предоставит вам текущее значение для этих динамических параметров (postId, category, productId).

Пример с Remix

Вы можете использовать хук useParam React Router для доступа к этим динамическим параметрам.

А как насчет маршрутов, которым нужно загружать файлы?

Представьте, что вам нужно, чтобы ваш сайт содержал типичные файлы robots.txt и sitemap.xml.

И в Next.js, и в Remix вы можете добавить их в каталог /public. Но Remix позволяет добиться этого через систему маршрутизации, просто создав /app/routes/[sitemap.xml].js или /app/routes/[robots.txt].js.

Загрузка данных

Оба фреймворка являются серверными, поэтому каждый из них имеет различные системы для получения данных и обработки вызовов API.

В случае Next.js вы можете выбрать один из двух вариантов, в зависимости от того, какой тип страницы вы хотите построить.

  • getStaticProps (SSG, ISR, если установлен интервал обновления) — получает данные во время сборки и предоставляет их в качестве реквизитов компонентов страницы.
export async function getStaticProps( {params} ) {
  const productList = await getProductList()

  return {
    props: {
        productList,
        slug: params.slug 
    }
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима
  • getServerSideProps (SSR) — выполняет поиск данных на стороне сервера во время выполнения и предоставляет возвращенные данные в качестве реквизитов компонентов страницы.
export async function getStaticProps( {params} ) {
  const productList = await getProductList()

  return {
    props: {
        productList,
        slug: params.slug 
    }
  }
}

/**
 * Here you would have your own getStaticProps/getServerSideProps functions
 * depending on what system you choose.
 **/

  const PageName = (props)=> {
      const { productList } = props
      // Here you could render the requested data
      return (<div>
        <pre> {JSON.stringify(productList, null, 4)} </pre>
      </div>)
  }
Вход в полноэкранный режим Выход из полноэкранного режима

Remix использует другой способ загрузки данных

  • Он предоставляет вам функцию loader для использования в каждом файле маршрута, которая будет работать на стороне сервера во время выполнения.
  • На самом компоненте страницы вы сможете использовать хук useLoaderData для сбора этих данных.
/**
 * Here you would have your own getStaticProps/getServerSideProps functions
 * depending on what system you choose.
 **/
export const async loader = ( {params} ) => {
    const productList = await getProductList()
    return {
        productList,
        slug: params.slug 
    }
};

export default function PageName() {
    const { productList } = useLoaderData();

    // Here you could render the requested data
    return (<div>
        <pre> {JSON.stringify(productList, null, 4)} </pre>
    </div>)
}
Вход в полноэкранный режим Выход из полноэкранного режима

Мутации данных

Next.js не имеет встроенного способа выполнения мутаций, и вам придется делать это вручную.

Remix же создал компонент формы и использует его так, как вы бы сделали это с родным элементом HTML-формы браузера. Если вы не вводите URL действия, он будет использовать тот же метод в качестве маршрута для формы.

Если метод — GET, то будет запущена экспортируемая функция loader, а если метод — POST, то будет запущена экспортируемая функция action, определенная в компоненте.

Кроме того, вы можете использовать предоставленный хук useTransition для управления поведением приложения в зависимости от статуса запроса без необходимости управлять им вручную, как обычно.

export const loader = async ({ params }) => {
  // Each time this page receive a GET Request, this loader will be executed
  const userData = await getSomeUserData(params.slug) 

  return {
    userData // it contains the User Name
  }
}

export const action = async ({ request }) => {
  // Each time this page receive a POST Request, this loader will be executed
  const form = await request.formData()
  const content = form.get('content')

  return redirect('/')
}

export default function App() {
  const { name } = useLoaderData();

  return (
    <div>
      <div>{`Hello there ${name}`}</div>

      <Form method="POST">
        <label htmlFor="content">
          Content: <textarea name="content" />
        </label>

        <input type="submit" value="Add New" />
      </Form>
    </div>
  )
}
Вход в полноэкранный режим Выход из полноэкранного режима

Стилизация

Next.js поставляется со встроенной поддержкой CSS из коробки, поэтому вы можете импортировать прямо в /pages/_app.js любой необходимый вам файл style.css.

Вы также можете добавить другие CSS фреймворки, с некоторыми конфигурациями или плагинами это довольно легко сделать.

Remix предпочитает сосредоточиться на более удобном для веб-стандартов решении, предоставляя компонент и экспортируемую функцию links.

import styles from "./styles/app.css";

export function links() {
  return [
      { rel: "stylesheet", href: styles },
      { rel: "stylesheet", href: 'https://.....' },
    ];
}

export default function App() {
  return (
     <html lang="en">
      <head>
        <Links />
      </head>
      <body>
        <Outlet />
      </body>
    </html>
  );
}
Вход в полноэкранный режим Выход из полноэкранного режима

Вы можете подумать, что это слишком — просто загрузить таблицу стилей, но что, если я скажу вам, что вы можете использовать ее на каждой странице отдельно, позволяя загружать определенные стили/шрифты только тогда, когда это необходимо?

Также здесь вы можете использовать другие CSS-фреймворки с небольшими настройками, например, Tailwindcss.

Оптимизация изображений

Здесь Next.js выигрывает, так как имеет собственный встроенный компонент next/image, который позволяет оптимизировать изображения, ленивую загрузку, контролировать размер и интегрировать загрузчики. К сожалению, на данный момент Remix не имеет никакой поддержки оптимизации изображений.

SEO

В начале этой статьи я рассказал вам, что одним из пунктов, стоящих за этими фреймворками, является решение SEO проблем SPA.

Оба они имеют свои собственные встроенные механизмы для динамического управления метаданными, которые должны использоваться на каждой странице, такими как ключевые слова, заголовки, описания и т.д.

В Next.js вы можете использовать их встроенный компонент next/head, импортировав его на свои страницы/маршруты.

import Head from 'next/head'

export default function PostPage() {
  return (
    <div>
      <Head>
        <title>Post Page Title</title>
        <meta name="description" content="Post Page Description" />
      </Head>
      <p>
        All the metadata inside Head component will be injected into the
        documents head
      </p>
    </div>
  )
}
Вход в полноэкранный режим Выход из полноэкранного режима

С другой стороны, Remix предоставляет нам экспортируемую функцию meta, которая может получать запрашиваемые данные страницы, а также параметры URL.

export const meta = ({ data, params }) => {
  charset: "utf-8",
  viewport: "width=device-width,initial-scale=1",
  title: `Post | ${data.title}`,
  description: data.description,
};

export default function PostPage() {
  return (
    <div>
      <p>
        All the metadata returned on meta function will be injected into the
        documents head
      </p>
    </div>
  )
}
Вход в полноэкранный режим Выход из полноэкранного режима

Обработка ошибок

Next.js дает вам возможность определять пользовательские страницы при возникновении определенных ошибок, например, 400 или 500. Но любая другая ошибка, помимо ошибок маршрутизации или статуса, приведет к разрыву страницы.

Remix оставляет все на волю вашего воображения, поэтому вы можете охватить и настроить любую ошибку по своему усмотрению. Они добавили Error Boundary, уникальную концепцию для обработки ошибок, представленную в React v16.

Вы можете легко закрыть эти ошибки, используя экспортируемые функции CatchBoundary и ErrorBoundary в вашем root.tsx или даже на каждой странице. Давайте добавим несколько наглядных примеров.

// Here you will catch any controlled error and will tell the framework what information should be shown
const CatchBoundary = () => {
  let caught = useCatch();

  switch (caught.status) {
    case 401:
      // Document is a custom React Component which contains <html>, <head>,<body>, etc
      return (
        <Document title={`${caught.status} ${caught.statusText}`}>
          <h1>
            {caught.status} {caught.statusText}
          </h1>
        </Document>
      );

    case 404:
      return <PageNotFoundComponent />; // Yes, you can also use a React Component
    default:
      throw new Error(
        `Unexpected caught response with status: ${caught.status}`
      );
  }
};

// Here you will have a more generic advise for when an uncontrolled error has been triggered
const ErrorBoundary = ( { error } ) => {
  return (
    <Document title="Uh-oh!">
      <h1>App Error</h1>
      <pre>{error.message}</pre>
      <p>
        Replace this UI with what you want users to see when your app throws
        uncaught errors.
      </p>
    </Document>
  );
};

export const loader: LoaderFunction = async ({ params }) => {
  const post = await getPost(params.slug);

  if (!post) {
    throw new Response("Not Found", {
      status: 404,
    });
  }

  return json({ post });
};

export default function PostPage() {
  const { post } = useLoaderData();
  return (
    <div>
      <pre>
        {JSON.stringify(post, null, 4)}
      </pre>
    </div>
  )
}
Вход в полноэкранный режим Выход из полноэкранного режима

Развертывание

Next.js легко развернуть на любом сервере, который поддерживает Node.js. У него также есть интеграция для бессерверного развертывания в Vercel, а Netlify имеет свой собственный адаптер, чтобы вы могли легко развернуть свое приложение.

Remix построен на Web Fetch API вместо Node.js, поэтому он может работать на Vercel, Netlify, Architect (серверы Node.js), а также в средах без Node.js, таких как Cloudflare.

Он также предоставляет вам адаптеры из коробки, когда вы начинаете свой проект, так что вы можете сразу перейти к делу, если вы знаете, где вы будете развертывать свой проект.

И один из самых важных моментов в Remix — это то, что он больше не использует Webpack. Вместо этого он использует Esbuild, который чрезвычайно быстр в пакетировании и развертывании.

Другие упоминания

Есть и другие моменты, которые необходимо учитывать, когда вы хотите выбрать Next.js или Remix, но я хотел собрать те, которые, на мой взгляд, имеют большее влияние при разработке проекта.

Но если вы хотите узнать немного больше, вот небольшой обзор того, о чем я не говорил в этой статье.

  • Живая перезагрузка. Next.js использует горячую перезагрузку модулей (HMR), и она включена по умолчанию, в то время как в Remix вам нужно добавить компонент в ваш root.tsx, чтобы заставить ваше приложение перезагружаться при каждом изменении кода.
  • Remix поддерживает Cookies, сессии и аутентификацию, в то время как Next.js этого не делает — вы должны использовать внешние библиотеки.
  • Next.js имеет встроенную поддержку оптимизации шрифтов и скриптов, а Remix — нет.
  • Обе программы позволяют использовать Typescript из коробки.
  • Remix работает с большинством своих функций без выполнения JavaScript, в то время как Next.js этого не делает.
  • Remix может содержать независимые маршруты внутри вложенных.
  • Оба могут быстро работать с Tailwindcss и имеют собственное руководство для достижения этого.
  • Next.js имеет встроенную поддержку интернационализированной маршрутизации.
  • Next.js имеет встроенную поддержку AMP, а Remix — нет.

Оцените статью
devanswers.ru
Добавить комментарий