Все статьи | Код

Next.js: что это такое и как его использовать

Next.js: что это такое и как его использовать главное изображение

Next.js — фреймворк на JavaScript, использующий React для построения Server Side Render-приложений (SSR) и статически-генерируемых сайтов. Наставник Хекслета Глеб Андреев рассказывает, кто создал Next.js, зачем его использовать и в чем его плюсы и минусы перед чистым React.

История Next.js

Фреймворк Next.js создан относительно недавно — в 2016 году внутри компании Vercel (ранее Zeit). Основная его задача — работа с Server Side Render-приложениями, написанными на React. Ее можно выполнять и самим с помощью ReactDOMServer и условного Express.js, но все-таки это не самый оптимальный способ, потому что разработчик в любом случае пишет много boilerplate-кода. Next.js выводит разработку SSR-приложений на следующий уровень и разбавляет ее различными оптимизациями.

В целом, SSR-приложения можно писать и самому, просто Next.js — более оптимизированный под такие задачи (Static Generation и так далее) фреймворк.

Работа Next.js следует шести основным принципам:

  1. Работа без настройки. Использование файловой системы в качестве API
  2. Только JavaScript. Все является функциями
  3. Автоматический Server Side Rendering и code-splitting
  4. Механизм получения данных определяется разработчиком
  5. Предзагрузка для увеличения производительности
  6. Простой деплой и развертывание

Спустя годы проект оброс фичами, в его разработку и оптимизацию внес вклад сам Google, а технологией пользуются крупные компании вроде Uber, Netflix, GitHub и других. И даже в самой документации React его включили как один из тул-чейнов для разработки.

Читайте также: Виталий Брагилевский: о преподавании, новой работе в JetBrains и книге про Haskell

Как устроен Next.js

Next.js — фреймворк, использующий библиотеку React. Главное его отличие от «чистого» React — в способе рендера конечных веб-страниц. Если React загружает минимальный HTML и зачастую большой бандл JS (иногда разделенный между страницами на модули), то Next.js использует Server Side Rendering — формирование первоначального HTML на стороне сервера, используя тот же самый React.

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

Давайте по шагам сравним процесс, происходящий после запроса страницы, используя Next.js и React. Как это выглядит на Next.js:

import Head from 'next/head'
import getProductData from "...";

export async function getServerSideProps(context) {
  const product = getProductData(context.params.id);

  return {
    props: {
      product
    },
  };
}

export default function Product({ product }) {
  return (
    <div>
      <Head>
        <title>Купить {product.name}</title>
      </Head>
      <main>
        <img src={product.image} alt="Product image" />
        <h1>{product.name}</h1>
        <p>{product.description}</p>
      </main>
    </div>
  )
}

Next.js:

  1. Браузер делает запрос на страницу с информацией о товаре, например, /product/1.
  2. Сервер получает запрос, загружает необходимые данные о товаре с другого сервера, формирует HTML на основе полученных данных и необходимых в данный момент компонентов React.
  3. Браузер сразу получает HTML с нужной информацией и показывает его пользователю, но JS для интерактивности еще не загружен.
  4. Подгрузка JS происходит после этого в фоновом режиме, после чего встраивается уже в имеющийся HTML-код — этот процесс называется hydration.

Как этот процесс выглядит на React:

import { Helmet } from "react-helmet";
import getProductData from "...";
import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";

const Product = () => {
  const { productId } = useParams();
  const [product, setProduct] = useState(null);

  useEffect(() => {
    getProductData(productId)
      .then(product => setProduct(product));
  }, [productId]);

  if (!product) {
    return <div>Загрузка</div>
  }

  return (
    <div>
      <Helmet>
        <title>Купить {product.name}</title>
      </Helmet>
      <main>
        <img src={product.image} alt="Product image" />
        <h1>{product.name}</h1>
        <p>{product.description}</p>
      </main>
    </div>
  )
}

export default Product;

React:

  1. Браузер делает запрос на страницу с информацией о товаре, например, /product/1.
  2. Сервер возвращает минимальный HTML-файл, в котором прописан импорт JS-файла, использующего React.
  3. После загрузки HTML начинается загрузка JS.
  4. Только после загрузки JS-файла происходит создание необходимых DOM-элементов, загрузка данных с сервера и отображение полезного контента.

Сравнив оба процесса, можно сразу заметить ключевые различия Next.js от React:

Плюсы Next.js:

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

Минусы Next.js:

  • Next.js загружает JS после HTML, до этого страница будет оставаться не интерактивной. Но даже так Next.js загрузит сначала минимально необходимый JS, а потом уже — весь остальной.
  • Необходимо больше серверных ресурсов, т.к. Next.js формирует HTML на стороне сервера перед отправкой его клиенту.

Разбираемся в архитектуре Next.js

Создать новое приложение на Next.js можно с помощью готового тулкита:

npx create-next-app my-app

npx позволяет запускать исполняемые npm-пакеты без предварительной установки. Подробнее в документации.

Эта команда создаст новое приложение в папке my-app и установит все необходимые зависимости:

Next.js «из коробки» поддерживает eslint и CSS-модули (в папке styles). Статические файлы можно складывать в папку public, как и в обычном React. Но самая главная для нас папка — pages.

Как Next.js формирует доступные URL и оптимизирует страницы.

Каждый файл внутри этой папки (кроме папки api, файлов _app и _document) воспринимается Next.js как отдельная страница. Важно об этом помнить (почему — расскажем чуть дальше). Так Next.js автоматически конвертирует файловую структуру в URL, включая динамические параметры. Станет понятнее, если посмотреть на пример:

Теперь посмотрим на пример файла users/[id]/index.js:

import Head from 'next/head'
import getUserData from "...";

export async function getServerSideProps(context) {
    const user = getUserData(context.params.id);

    return {
        props: {
            user: user
        },
    };
}

export default function User({user}) {
    return (
        <div>
            <Head>
                <title>Страница {user.name}</title>
            </Head>
            <main>
                <img src={user.avatar} alt="User photo"/>
                <h1>{user.name}</h1>
                <p>{user.bio}</p>
            </main>
        </div>
    )
}

Здесь мы видим дефолтный экспорт (многое в Next.js работает через экспорты) основного компонента User, который выводит информацию о пользователе. Эту информацию он берет из props, но как она туда попадает?

Тут Next.js дает нам два варианта, которые приводят к разному результату. Оба варианта работают как именованные экспорты:

  1. getServerSideProps будет вызвана на каждый запрос во время работы приложения. Внутри можно делать вызовы к внешним сервисам и API, чтобы получить последнюю актуальную информацию. В зависимости от того, заходит человек на сайт впервые или просто перемещается между страницами, этот вызов будет сделан либо на сервере, либо на клиенте.
  2. getStaticProps будет вызвана один раз при сборке приложения, она подготовит готовые HTML-файлы. Скажем, у вас есть список статей, которые обновляются редко. Их можно заранее отрендерить с помощью getStaticProps, и эти страницы будут быстрее загружаться, так как вся информация у них уже есть.

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

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

Подробнее о способах оптимизации страниц и том, как правильно их использовать, можно почитать в документации.

Директория API

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

URL для доступа к API строится по тому же принципу, что и URL для страниц. Структура из примера формирует путь api/users/[id], к которому можно делать запросы с фронтенда. Давайте посмотрим на пример работы:

const users = {
    "1": { name: "Билл Гейтс", bio: "Создатель Windows", avatar: "https://..." },
    "2": { name: "Стив Джобс", bio: "Основатель Apple", avatar: "https://..." },
    "3": { name: "Джон Кармак", bio: "Разработчик Doom", avatar: "https://..." },
}

export default function handler(req, res) {
    const { id } = req.query;
    res.status(200).json(users[id]);
}

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

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

Немного про _app и _document

_app и _document используются для кастомизации всех страниц одновременно, но служат разным функциям.

_app нужен для построения общей логики между всеми страницами. Например, сюда можно подключить Redux, сервисы аутентификации, различные кэши и так далее. Если вы используете getServerSideProps или getInitialProps (его старший deprecated брат) внутри _app, то все ваши страницы начинают обрабатываться в runtime — и это отключает статическую оптимизацию (и NextJS поспешит вам об этом сообщить).

_document нужен для редактирования HTML-шаблона, который используется при рендере всех страниц. Чаще всего вам не понадобится добавлять и изменять его, но он нужен для подключения css-in-js или рендера различных статических блоков html, которые всегда будут в шаблоне страницы.

Дополнительно

Next.js предоставляет функции и компоненты, которые нужны для определенных случаев. Краткое описание самых часто используемых:

  • next/router — модуль для работы с роутером Next.js. Нужен для программных редиректов, получения данных из URL на клиенте.

  • next/link — компонент Link, который рендерит «правильные» ссылки. В HTML-разметке они будут обычными тегами <a> и сработают, даже если JS у пользователя отключен. Если JS включен, то по клику произойдет переход с помощью клиентского рендера и оптимизаций Next.js.

  • next/head — компонент Head, который позволяет изменять контент тега <head> без оглядки на серверный/клиентский рендер.

  • next/script — компонент Script для подключения внешних скриптов на клиенте (например, метрик).

Развитие и будущее фреймворка

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

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

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

Аватар пользователя Глеб Андреев
Глеб Андреев 15 сентября 2022
Рекомендуемые программы

С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.

Иконка программы Фронтенд-разработчик
Профессия
Разработка фронтенд-компонентов для веб-приложений
6 октября 10 месяцев
Иконка программы Python-разработчик
Профессия
Разработка веб-приложений на Django
6 октября 10 месяцев
Иконка программы Java-разработчик
Профессия
Разработка приложений на языке Java
6 октября 10 месяцев
Иконка программы PHP-разработчик
Профессия
Разработка веб-приложений на Laravel
6 октября 10 месяцев
Иконка программы Node.js-разработчик
Профессия
Разработка бэкенд-компонентов для веб-приложений
6 октября 10 месяцев
Иконка программы Верстальщик
Профессия
Верстка с использованием последних стандартов CSS
в любое время 5 месяцев
Иконка программы Fullstack-разработчик
Профессия
Разработка фронтенд- и бэкенд-компонентов для веб-приложений
6 октября 16 месяцев
Иконка программы Разработчик на Ruby on Rails
Профессия
Создание веб-приложений со скоростью света
6 октября 5 месяцев
Иконка программы Инженер по тестированию
Профессия
Новый
Ручное тестирование веб-приложений
дата определяется 4 месяца