BackНазад к блогу

Next.js 8

Next.js 8 представляет Serverless Mode, уменьшенные размеры бандлов, улучшения производительности и многое другое.

Сегодня мы с гордостью представляем готовую к production-использованию версию Next.js 8, включающую:

Как всегда, мы стремились обеспечить полную обратную совместимость всех этих улучшений. Для большинства приложений Next.js достаточно выполнить:

Terminal
npm i next@latest react@latest react-dom@latest

Мы благодарны нашему сообществу и всем, кто сделал ставку на наш успех. С момента нашего последнего блог-поста такие компании, как AT&T, Starbucks и Twitch, перезапустили свои публичные сайты и приложения с использованием Next.js.

Serverless Next.js

Цель Serverless в Next.js преобразует страницы в Serverless-функции

Serverless-развертывание значительно улучшает надежность и масштабируемость, разбивая ваше приложение на меньшие части (также называемые лямбдами). В случае Next.js каждая страница в директории pages становится serverless-лямбдой.

У serverless есть ряд преимуществ. В указанной ссылке обсуждаются некоторые из них в контексте Express, но принципы применимы повсеместно: serverless позволяет распределять точки отказа, обеспечивает бесконечную масштабируемость и является очень экономичным с моделью "плати за то, что используешь".

Чтобы включить serverless mode в Next.js, добавьте serverless в target в next.config.js:

next.config.js
module.exports = {
  target: 'serverless',
};

Цель serverless будет выводить одну лямбду на страницу. Этот файл полностью автономен и не требует никаких зависимостей для работы:

  • pages/index.js => .next/serverless/pages/index.js
  • pages/about.js => .next/serverless/pages/about.js

Сигнатура Serverless-функции Next.js похожа на callback HTTP-сервера Node.js:

type Function = (req: http.IncomingMessage, res: http.ServerResponse) => void;
  • http.IncomingMessage
  • http.ServerResponse
  • void означает, что функция не возвращает значение и эквивалентна undefined в JavaScript. Вызов функции завершит запрос.

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

Например, если платформа поддерживает класс http.Server Node.js:

server.js
const http = require('http');
const page = require('./.next/serverless/about.js');
const server = new http.Server((req, res) => page.render(req, res));
server.listen(3000, () => console.log('Listening on http://localhost:3000'));

Итог

  • Низкоуровневый API для реализации serverless-развертывания
  • Каждая страница в директории pages становится serverless-функцией (лямбдой)
  • Создает максимально компактную serverless-функцию (50 KB базовый размер zip)
  • Оптимизирована для быстрого холодного старта функции
  • Serverless-функция не имеет зависимостей (они включены в бандл функции)
  • Использует http.IncomingMessage и http.ServerResponse из Node.js
  • Включается через target: 'serverless' в next.config.js
  • Цель server по-прежнему полностью поддерживается
  • publicRuntimeConfig и serverRuntimeConfig не поддерживаются в режиме serverless. Вместо этого используйте конфигурацию во время сборки.

Значительное сокращение использования памяти при сборке

Мы внесли вклад в webpack, чтобы улучшить производительность сборки Next.js (и всей экосистемы webpack!) и использование ресурсов.

В результате было достигнуто 16-кратное улучшение использования памяти без ухудшения производительности.

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

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

Конфигурация окружения во время сборки

При анализе приложений Next.js мы часто наблюдали использование babel-plugin-transform-define или webpack.DefinePlugin для предоставления конфигурационных значений приложению.

В Next.js 8 мы вводим новый ключ env в next.config.js для предоставления той же функциональности с обратной совместимостью:

next.config.js
module.exports = {
  env: {
    customKey: 'MyValue',
  },
};

Это позволит вам использовать process.env.customKey в вашем коде. Например:

pages/index.js
export default function IndexPage() {
  return <h1>Значение customKey: {process.env.customKey}</h1>;
}

process.env.customKey будет заменен на 'MyValue' во время сборки.

Улучшения производительности предзагрузки

Роутер Next.js позволяет предзагружать страницы для более быстрой навигации:

pages/index.js
import Link from 'next/link';
 
export default function IndexPage() {
  return (
    <>
      <Link href="/about" prefetch>
        <a>На страницу About</a>
      </Link>
    </>
  );
}

Это работает путем предзагрузки JavaScript-бандла для каждой ссылки с атрибутом prefetch.

В версиях до Next.js 8 это означало вставку тега <script> в <body> документа.

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

В Next.js 8 prefetch использует <link rel="preload"> вместо тега <script>. Кроме того, предзагрузка начинается только после onload, чтобы позволить браузеру управлять ресурсами.

Дополнительно Next.js теперь определяет 2G-интернет и режим navigator.connection.saveData, чтобы отключать предзагрузку на медленных соединениях.

Уменьшенный размер начального HTML

Поскольку Next.js предварительно рендерит HTML, он оборачивает страницы в стандартную структуру с <html>, <head>, <body> и JavaScript-файлами, необходимыми для рендеринга страницы.

В Next.js 7 мы оптимизировали начальный размер до 1.50KB, что на 7.4% меньше, чем в предыдущей версии.

Нам удалось дополнительно уменьшить начальный размер до 1.16KB, что на 23% меньше:

7.08.0разница
Размер документа (серверный рендеринг)1.50KB1.16KB23% меньше

Основные способы, которыми мы сократили размер:

  • Удален inline-скрипт инициализации страницы
  • Страница /_error больше не включается при каждой загрузке страницы

Загрузка /_error по требованию

При возникновении ошибки в production рендерится страница /_error для отображения информации об ошибке.

С самого первого релиза Next.js тег скрипта страницы /_error был частью начального HTML, что означало его загрузку даже если не было ошибок во время выполнения.

Начиная с Next.js 8 страница /_error загружается по требованию при возникновении ошибки.

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

Улучшения DX (Developer Experience)

Одна из основных целей Next.js — обеспечить лучшую производительность в production с наилучшим опытом разработчика. Этот релиз включает множество тонких улучшений, основанных на отзывах пользователей.

Улучшенные on-demand entries

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

Например, при посещении http://localhost:3000/my-page файл pages/my-page.js компилируется по требованию, после чего страница рендерится.

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

Поток on-demand entries

Поток on-demand entries

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

Next.js отслеживает доступ к страницам с помощью механизма опроса. Каждые 5 секунд отправляется "on-demand-entries-ping", чтобы сервер разработки Next.js знал, что к данной странице обращаются.

С момента первоначального релиза этой функции опрос выполнялся с помощью вызова window.fetch, что означало, что каждый раз при срабатывании опроса он отображался в инструментах разработчика браузера на вкладках console и network.

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

Мы рады сообщить, что в Next.js 8 опрос на основе fetch был заменен подходом на основе WebSockets, что означает, что опросы по-прежнему происходят, но видны только при инспектировании соединения WebSocket.

Особая благодарность JJ Kasper за сотрудничество при переходе на WebSockets.

Быстрое прослушивание порта в разработке

При запуске сервера разработки Next.js необходимо выполнить первоначальную компиляцию, чтобы иметь возможность обслуживать запросы. По умолчанию Next.js ожидал завершения этого шага компиляции перед запуском HTTP-сервера, что означало, что если вы запускали next dev и затем переходили в браузер, иногда можно было увидеть сообщение "This site can’t be reached", потому что HTTP-сервер еще не прослушивал соединения.

В Next.js 8 HTTP-сервер будет прослушивать соединения до начала компиляции, что означает, что если вы перейдете на http://localhost:3000/ до завершения компиляции, запрос будет ожидать завершения первоначальной компиляции перед обслуживанием, вместо необходимости обновлять страницу, пока она не станет доступной.

Особая благодарность Brian Beck за реализацию этой функции.

Более быстрый Static Export

Next.js фокусируется на идее предварительного рендеринга как средства достижения высокой производительности. Предварительный рендеринг бывает двух видов:

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

Начиная с Next.js 8, статический рендеринг через next export будет работать быстрее, если ваша машина имеет несколько CPU.

На основе тестов на MacBook с 4 ядрами CPU скорость экспорта увеличилась с примерно 25 страниц в секунду до 75 страниц в секунду за счет использования всех ядер для предварительного рендеринга страниц.

Next.js автоматически определит количество ядер CPU и распределит страницы соответствующим образом, никаких изменений кода не требуется.

Особая благодарность Benjamin Kniffler за реализацию этой функции.

Дедупликация элементов Head

Общая потребность при создании приложений — обновление элемента <head> страницы. Например, для установки <title> или <meta name="viewport"> для адаптивного дизайна.

Next.js предоставляет встроенный компонент для внесения изменений в <head>:

pages/index.js
import Head from 'next/head';
 
export default function IndexPage() {
  return (
    <>
      <Head>
        <title>Заголовок моей страницы</title>
      </Head>
    </>
  );
}

Компонент <Head> можно использовать даже несколько раз в разных компонентах, например, ваш компонент макета может устанавливать некоторые теги head по умолчанию.

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

По этой причине теперь можно указать свойство key для каждого элемента внутри компонента <Head>, что автоматически дедуплицирует теги с одинаковым значением key.

При установке key="viewport" на двух тегах будет отрендерен только последний.

pages/index.js
import Head from 'next/head';
export default function IndexPage() {
  return (
    <>
      <Head>
        <title>Заголовок моей страницы</title>
        <meta
          name="viewport"
          content="initial-scale=1.0, width=device-width"
          key="viewport"
        />
      </Head>
      <Head>
        <meta
          name="viewport"
          content="initial-scale=1.2, width=device-width"
          key="viewport"
        />
      </Head>
    </>
  );
}

Улучшения безопасности

Новая опция конфигурации crossOrigin

В Next.js 6 мы представили возможность добавления атрибута crossOrigin к <Head> и <NextScript> в pages/_document.js, однако это не охватывало все случаи использования для установки cross-origin.

Next.js имеет клиентский роутер, который динамически вставляет теги <script>, этим тегам не хватало атрибута cross-origin при вставке.

Чтобы гарантировать, что все теги <script> имеют установленный cross-origin, мы ввели новую опцию конфигурации в next.config.js

next.config.js
module.exports = {
  crossOrigin: 'anonymous',
};

Еще одно преимущество введения этой опции заключается в том, что больше не нужен пользовательский pages/_document.js для настройки cross-origin в вашем приложении.

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

Удаление встроенного JavaScript

При использовании Next.js 7 и ниже для включения Политики безопасности контента (CSP) пользователям приходилось добавлять script-src 'unsafe-inline' в свою политику, потому что Next.js создавал встроенный тег <script> для передачи данных, например, для передачи результата getInitialProps на клиентскую сторону.

В Next.js 8 мы заменили этот встроенный тег скрипта на JSON-тег для безопасной передачи на клиент. Это означает, что Next.js больше не включает встроенные скрипты.

Теперь с осторожностью можно использовать script-src 'self'.

Пример аутентификации API

Один из самых запрашиваемых примеров за все время — это как реализовать аутентификацию в Next.js для внешнего API, любого API, на любом языке программирования.

С выходом Next.js 8 мы также представляем новый пример: with-cookie-auth

Этот пример показывает, как выполнить аутентификацию для внешнего Node.js API, но применяемые концепции работают для любого API без сохранения состояния.

В примере используется cookie для обмена токеном между серверным и клиентским рендерингом.

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

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

Сообщество

С момента первого релиза Next.js использовался во всём — от компаний из списка Fortune 500 до личных блогов. Мы очень рады видеть продолжающийся рост популярности Next.js.

  • Более 600 участников внесли как минимум 1 коммит.
  • На GitHub проект получил более 34 400 звёзд.
  • С момента первого релиза было отправлено более 2600 pull request'ов.

Сообщество Next.js насчитывает более 4 570 участников. Присоединяйтесь!