Сегодня мы с гордостью представляем готовую к production-использованию версию Next.js 8, включающую:
- Serverless Next.js
- Значительное сокращение использования памяти при сборке
- Конфигурация окружения во время сборки
- Улучшения производительности предзагрузки
- Уменьшенный размер начального HTML
- Улучшенные on-demand entries
- Быстрое прослушивание порта в разработке
- Более быстрый Static Export
- Дедупликация элементов Head
- Новая опция конфигурации crossOrigin
- Удаление inline JavaScript
- Пример аутентификации API
Как всегда, мы стремились обеспечить полную обратную совместимость всех этих улучшений. Для большинства приложений Next.js достаточно выполнить:
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
:
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:
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
для предоставления той же функциональности с обратной совместимостью:
module.exports = {
env: {
customKey: 'MyValue',
},
};
Это позволит вам использовать process.env.customKey
в вашем коде. Например:
export default function IndexPage() {
return <h1>Значение customKey: {process.env.customKey}</h1>;
}
process.env.customKey
будет заменен на 'MyValue'
во время сборки.
Улучшения производительности предзагрузки
Роутер Next.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.0 | 8.0 | разница | |
---|---|---|---|
Размер документа (серверный рендеринг) | 1.50KB | 1.16KB | 23% меньше |
Основные способы, которыми мы сократили размер:
- Удален 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
Если к странице не обращались в течение 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>
:
import Head from 'next/head';
export default function IndexPage() {
return (
<>
<Head>
<title>Заголовок моей страницы</title>
</Head>
</>
);
}
Компонент <Head>
можно использовать даже несколько раз в разных компонентах, например, ваш компонент макета может устанавливать некоторые теги head по умолчанию.
Однако вам может потребоваться переопределить теги head по умолчанию другим значением. В более ранних версиях Next.js это приводило к дублированию тега в выводе, так как не было возможности дедуплицировать теги.
По этой причине теперь можно указать свойство key
для каждого элемента внутри компонента <Head>
, что автоматически дедуплицирует теги с одинаковым значением key
.
При установке key="viewport"
на двух тегах будет отрендерен только последний.
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
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 участников. Присоединяйтесь!