После нескольких лет работы с React во внешнем интерфейсе недавно я исследовал что-то новое в виде Next.js.

Next.js называет себя React framework for Production. По сути, это упрощает создание веб-приложений с помощью React. Некоторые из основных функций, которые он предлагает, довольно убедительны:

  • Рендеринг на стороне сервера (SSR)
  • Генерация статических сайтов (SSG)
  • Инкрементальная статическая регенерация (ISR)
  • Автоматическое разделение кода

Обычно сайты React отображаются в браузере в процессе, называемом Отображение на стороне клиента (CSR). У этого есть некоторые недостатки, самый большой из которых заключается в том, что ботам и поисковым роботам сложно правильно увидеть сайт, и это сильно влияет на SEO, поскольку все поисковые системы и многие другие сайты используют ботов для индексации контента веб-сайтов.

При использовании рендеринга на стороне сервера (SSR) наш сервер отображает страницу React напрямую в HTML, в то время как React также работает на стороне клиента, чтобы сделать страницу интерактивной посредством процесса, называемого гидратацией. Боты могут напрямую получать сгенерированный HTML-код и использовать его для индексации нашего сайта. Недостаток: больше задач для нашего сервера, что означает большую нагрузку, и нам может понадобиться больше серверов для масштабирования.

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

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

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

Создание приложения

Создать приложение очень просто благодаря утилите create-next-app:

npx create-next-app@latest

Это быстро установит все зависимости и настроит пару команд для запуска приложения. Чтобы запустить его для разработки, мы можем запустить команду npm run dev.

Глядя на структуру проекта, наиболее важными являются папка pages, в которую мы будем помещать наши страницы, и файл next.config.js для настройки. .

Создание страницы

Next.js предлагает систему маршрутизации на основе страниц, которая работает как на стороне клиента, так и на стороне сервера. Начнем с добавления простой страницы в папку pages:

После сохранения мы сможем получить доступ к странице по адресу http://localhost:3000/my-page.

Содержимое страницы реализовано как компонент React, а имя файла страницы — это его URL-адрес! В нашем случае файл с именем my-page.js можно найти по адресу /my-page.

Довольно мило, но давайте добавим к нему некоторые данные.

Получение данных на стороне клиента

На стороне клиента мы можем получать данные так же, как и в любом приложении React. У команды Next.js есть служебный хук React под названием SWR для выборки данных, но в этом примере мы будем использовать стандартный API fetch(). Итак, что мы собираемся получить? Покемон, конечно! И мы получим его от PokeApi.

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

Наша страница должна выглядеть… мило :)

Получение данных на сервере для SSR

Давайте посмотрим, как мы можем изменить эту выборку данных на сервере. Это означает, что мы входим на территорию рендеринга на стороне сервера.

Способ Next.js сделать это — экспортировать асинхронную функцию с именем getServerSideProps(), все, что нам нужно сделать, — это переместить туда наш вызов fetch, и наш компонент получит его как опору.

И после обновления наша страница должна выглядеть так же, как и раньше… мило! Ради интереса давайте также отключим JavaScript в нашем браузере. В Chrome это можно сделать в инструментах разработчика, нажав Ctrl+Shift+P и выполнив поиск команды Отключить JavaScript:

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

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

Получение данных во время сборки для SSG

Оказывается, для этого нужно не так много изменений. Вместо getServerSideProps() нам придется реализовать getStaticProps():

Наша страница и даже код получения данных должны выглядеть так же, как и раньше!

Для включения добавочной регенерации сайта требуется лишь небольшое изменение:

Возвращенный объект теперь имеет дополнительное поле под названием revalidate со значением 120. Это сообщает Next.js, что наша статическая страница действительна в течение 120 секунд. Когда запрос поступает по истечении этого времени, старая версия по-прежнему возвращается, но Next.js также повторно генерирует страницу в фоновом режиме. Довольно аккуратно!

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

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

Желаемый путь для этих страниц будет /pokemon/name-of-the-pokemon, например /pokemon/charizard.

Мы можем создать папку с именем pokemon и сохранить имя покемона в качестве параметра маршрута. В результате имя файла на первый взгляд кажется немного странным: pages/pokemon/[name].js

Рендеринг на стороне сервера с динамическими маршрутами

Начнем с рендеринга страницы на стороне сервера. В getServerSideProps() мы фактически получаем параметр, который я еще не упомянул, называемый context. Он содержит информацию о текущем запросе. Параметры маршрутизации доступны через context.params.[name-of-the-parameter] или в нашем случае context.params.name. Там же доступны параметры строки запроса.

Переход по адресу http://localhost:3000/pokemon/charizard приведет к следующему:

Но что произойдет, если наш покемон не существует? В идеале мы должны вернуть ответ 404 Not Found. Next.js также делает это возможным, возвращая notFound: true из getServerSideProps:.

Генерация статического сайта с динамическими маршрутами

Это немного сложно. Мы говорим о статических сайтах, но с динамическими маршрутами. Разве это не немного противоречиво? К счастью, Next.js предлагает решение этой головоломки.

Давайте начнем с реализации getStaticProps() таким же образом, как и раньше, с добавлением дополнительной обработки 404 и параметра контекста, точно так же, как в getServerSideProps():

Это само по себе приводит к приятной маленькой ошибке:

Error: getStaticPaths is required for dynamic SSG pages and is missing for '/pokemon/[name]'.

Получается, что для статической генерации нам нужно сообщить Next.js, какие значения может принимать наш параметр [name]. Это можно сделать, внедрив getStaticPaths().

В путях функция возвращает список доступных путей. Он сообщает Next.js, что наш параметр [name] может принимать значение "charizard" или "pikachu".

При этом появится чаризард или пикачу (в зависимости от URL-адреса), но как насчет других? Давайте попробуем это с Bulbasaur: переход на http://localhost:3000/pokemon/bulbasaur s должен привести к странице 404.

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

К счастью, есть и другой способ: резервное значение, возвращенное из getStaticPaths(). Он может принимать 3 разных значения и влияет на отображение нашей страницы: false, blocking или >правда.

В предыдущем примере мы установили fallback в false. Это сообщает Next.js, что у нас есть все значения, которые мы хотим отобразить, внутри массива paths. Другими словами, это говорит Next.js, что он не должен выполнять какие-либо резервные действия при доступе к URL-адресу, отличному от того, который мы описали в массиве paths .

Давайте посмотрим, что другие резервные варианты могут сделать для нас…

Рендеринг статических страниц на сервере с откатом: «блокировка»

Давайте немного углубимся в территорию ISR, изменив ее на резервный вариант: «блокировка».

После быстрого обновления мы должны увидеть следующее:

Как сейчас выглядит Бульбазавр? Мы не вернули его из getStaticPaths(), но Next.js по-прежнему может правильно отображать страницу.

По сути, запасной вариант: «блокировка» говорит Next.js, что если он обнаружит запрос к пути, которого нет в массиве paths, он должен заблокировать запрос и сделать его подождите, пока он попытается отобразить страницу на сервере. Это по-прежнему статический рендеринг, который происходит только один раз, но не во время сборки, а на сервере при первом доступе к странице. После первоначального рендеринга ответ будет отправлен клиенту.

Next.js отобразит страницу точно так же, как она была бы создана во время сборки: он вызовет getStaticProps() с bulbasaur, указанным в параметре, и подаст возвращенные реквизиты нашему компоненту страницы.

Делая это, мы используем форму Инкрементной регенерации сайта: мы можем решить, что хотим отображать некоторые части нашего сайта во время сборки, а другие лениво, при первом входящем запросе. Это также можно комбинировать с опцией revalidate, которая обсуждалась ранее.

Рендеринг статических страниц на сервере с откатом: true

Последним вариантом для резервного варианта является установка true. Это говорит Next.js отображать резервную страницу, в то время как статическая страница создается в фоновом режиме. Это требует небольшого изменения в компоненте React на странице, и давайте также добавим задержку в пару секунд, чтобы убедиться, что мы действительно видим запасной вариант:

После обновления в течение нескольких секунд отображается резервная страница, а затем появляется реальная страница. В компоненте React мы можем определить, хочет ли Next.js, чтобы мы отображали резервную страницу, проверив флаг router.isFallback. Чтобы получить объект маршрутизатора, мы можем вызвать хук useRouter().

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

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

Более того, Next.js даже автоматически переключается на откат: «блокировку» для ботов, поэтому мы даже не жертвуем SEO, используя этот подход.

Отрисовываются ли страницы на клиенте?

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

  • Как упоминалось ранее, это может произойти, когда резервная страница переключается на фактическую, и мы используем fallback: true.
  • Это также может произойти во время навигации на стороне клиента. Для навигации на стороне клиента мы можем использовать next/link, и это хороший компонент Link.
  • Когда мы используем выборку данных на стороне клиента.

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

Как это происходит? Оказывается, нам не нужно делать для этого ничего особенного, Next.js позаботится обо всем этом в фоновом режиме. Данные, возвращенные из getStaticProps() или getServerSideProps(), будут получены с сервера в виде объекта JSON, а наш компонент автоматически отобразится на клиенте.

Посмотреть в действии

Все примеры также доступны на StackBlitz благодаря их действительно классной технологии WebContainers. Проверьте это здесь: https://stackblitz.com/edit/nextjs-cwnvrj?file=README.md

Что дальше?

Что ж, на сегодня это все, но есть еще более приятные функции, которые здесь не рассматриваются, например:

  • API-маршруты
  • Универсальные маршруты
  • Оптимизация изображений, шрифтов и скриптов
  • Компонент ссылки
  • Главный компонент
  • ПО промежуточного слоя
  • И это еще не все!

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

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

Я все еще пытаюсь понять, как можно структурировать крупномасштабное приложение Next.js, но это не должно быть слишком сложно, если следовать некоторым стандартным рекомендациям и шаблонам.

Буду ли я рассматривать Next.js для некоторых будущих проектов? Определенно! На самом деле я планирую использовать его в ближайшем будущем для небольшого веб-сайта. :)

Итак, что ждет вас дальше?