Разрушение громоздких сборок с помощью Netlify и Next.js
Опубликовано: 2022-03-10Эта статья была любезно поддержана нашими дорогими друзьями из Netlify, которые представляют собой разнообразную группу невероятных талантов со всего мира и предлагают платформу для веб-разработчиков, которая увеличивает производительность. Спасибо!
Одной из самых больших проблем при работе со статически сгенерированными веб-сайтами является постепенное замедление сборки по мере роста вашего приложения. Это неизбежная проблема, с которой рано или поздно сталкивается любой стек, и она может возникать с разных сторон в зависимости от того, с каким продуктом вы работаете.
Например, если ваше приложение имеет несколько страниц (представлений, маршрутов) при создании артефакта развертывания, каждый из этих маршрутов становится файлом. Затем, когда вы достигнете тысяч, вы начнете задаваться вопросом, когда вы сможете развернуться без необходимости планировать заранее. Этот сценарий распространен на платформах электронной коммерции или блогах, которые уже составляют большую часть сети, но не всю ее. Однако маршруты — не единственное возможное узкое место.
Ресурсоемкое приложение также в конечном итоге достигнет этой поворотной точки. Многие статические генераторы выполняют оптимизацию активов, чтобы обеспечить наилучшее взаимодействие с пользователем. Без оптимизации сборки (инкрементные сборки, кэширование, мы скоро доберемся до этого) это в конечном итоге также станет неуправляемым — подумайте о просмотре всех изображений на веб-сайте: изменение размера, удаление и/или создание новых файлов снова и снова. И как только все это будет сделано: помните, что Jamstack обслуживает наши приложения с границ сети доставки контента . Так что нам все еще нужно переместить вещи с сервера, на котором они были скомпилированы, на края сети.

Вдобавок ко всему, есть еще один факт: данные часто являются динамическими, а это означает, что когда мы создаем наше приложение и развертываем его, это может занять несколько секунд, несколько минут или даже час. Тем временем мир продолжает вращаться, и если мы получаем данные откуда-то еще, наше приложение обязательно устареет. Неприемлемый! Постройте снова, чтобы обновить!
Создайте один раз, обновляйте по мере необходимости
Решение громоздких сборок какое-то время было в центре внимания практически для каждой платформы, фреймворка или сервиса Jamstack . Многие решения основаны на добавочных сборках. На практике это означает, что сборки будут такими же громоздкими, как и различия, которые они несут с текущим развертыванием.
Однако определение алгоритма сравнения — непростая задача. Чтобы конечный пользователь действительно выиграл от этого улучшения, необходимо учитывать стратегии аннулирования кеша. Короче говоря: мы не хотим аннулировать кеш для страницы или ресурса, который не изменился.
Next.js придумал добавочную статическую регенерацию ( ISR ). По сути, это способ объявить для каждого маршрута, как часто мы хотим, чтобы он перестраивался. Под капотом это упрощает большую часть работы на стороне сервера. Потому что каждый маршрут (динамический или нет) будет перестраиваться сам с учетом определенного периода времени, и он просто идеально вписывается в аксиому Jamstack об аннулировании кеша при каждой сборке. Думайте об этом как о заголовке max-age
, но для маршрутов в вашем приложении Next.js.
Чтобы запустить ваше приложение, ISR находится всего в одном свойстве конфигурации. В вашем компоненте маршрута (внутри каталога /pages
) перейдите к методу getStaticProps
и добавьте ключ revalidate
проверки в возвращаемый объект:
export async function getStaticProps() { const { limit, count, pokemons } = await fetchPokemonList() return { props: { limit, count, pokemons, }, revalidate: 3600 // seconds } }
Вышеприведенный фрагмент гарантирует, что моя страница будет перестраиваться каждый час и получать больше покемонов для отображения.
Мы по-прежнему время от времени получаем массовые сборки (при выпуске нового развертывания). Но это позволяет нам отделить контент от кода, перемещая контент в систему управления контентом (CMS), мы можем обновлять информацию за несколько секунд, независимо от размера нашего приложения. До свидания, вебхуки для исправления опечаток!
Строители по требованию
Netlify недавно запустила On-Demand Builders, который является их подходом к поддержке ISR для Next.js, но также работает с платформами, включая Eleventy и Nuxt. На предыдущем занятии мы установили, что ISR — это большой шаг к сокращению времени сборки и рассмотрению значительной части сценариев использования. Тем не менее, были оговорки:
- Полные сборки при непрерывном развертывании.
Инкрементный этап происходит только после развертывания и для данных. Невозможно отправлять код постепенно - Инкрементальные сборки — это продукт времени.
Кэш становится недействительным на временной основе. Таким образом, могут возникнуть ненужные сборки или необходимые обновления могут занять больше времени в зависимости от периода повторной проверки, установленного в коде.
Новая инфраструктура развертывания Netlify позволяет разработчикам создавать логику для определения того, какие части их приложения будут создаваться при развертывании, а какие части будут отложены (и как они будут отложены).
- Критический
Никаких действий не требуется. Все, что вы развертываете, будет построено на push . - Отложено
Конкретная часть приложения не будет создаваться при развертывании, она будет отложена для создания по требованию всякий раз, когда происходит первый запрос, а затем она будет кэшироваться как любой другой ресурс этого типа.
Создание конструктора по запросу
Прежде всего, добавьте в проект пакет netlify/functions в качестве devDependency
:
yarn add -D @netlify/functions
Как только это будет сделано, это будет похоже на создание новой функции Netlify. Если вы не установили для них конкретный каталог, перейдите в netlify/functions/
и создайте файл с любым именем для вашего билдера.
import type { Handler } from '@netlify/functions' import { builder } from '@netlify/functions' const myHandler: Handler = async (event, context) => { return { statusCode: 200, body: JSON.stringify({ message: 'Built on-demand! ' }), } } export const handler = builder(myHandler)
Как вы можете видеть из приведенного выше фрагмента, сборщик по запросу отделяется от обычной функции Netlify, потому что он заключает свой обработчик в метод builder()
. Этот метод связывает нашу функцию с задачами сборки. И это все, что вам нужно, чтобы часть вашего приложения откладывалась для сборки только тогда, когда это необходимо. Небольшие добавочные сборки с самого начала!
Next.js на Netlify
Чтобы создать приложение Next.js на Netlify, необходимо добавить 2 важных плагина, чтобы в целом улучшить работу: Netlify Plugin Cache Next.js и Essential Next-on-Netlify. Первый более эффективно кэширует ваш NextJS, и вам нужно добавить его самостоятельно, а второй вносит несколько небольших изменений в то, как построена архитектура Next.js, чтобы она лучше соответствовала Netlify и была доступна по умолчанию для каждого нового проекта, который Netlify может определить. с помощью Next.js.

Строители по запросу с Next.js
Повышение производительности, производительность развертывания, кэширование, опыт разработчиков. Это все очень важные темы, но их много — и нужно время, чтобы их правильно настроить. Затем мы возвращаемся к старой дискуссии о том, чтобы сосредоточиться на опыте разработчиков, а не на опыте пользователя. Это время, когда вещи отправляются в скрытое место в бэклоге, чтобы их забыли. Не совсем.
Netlify прикроет вашу спину. Всего за несколько шагов мы можем использовать всю мощь Jamstack в нашем приложении Next.js. Пришло время закатать рукава и собрать все воедино.
Определение предварительно визуализированных путей
Если вы уже работали со статической генерацией внутри Next.js, вы, вероятно, слышали о методе getStaticPaths
. Этот метод предназначен для динамических маршрутов (шаблонов страниц, которые будут отображать широкий диапазон страниц). Не слишком останавливаясь на тонкостях этого метода, важно отметить, что тип возвращаемого значения — это объект с двумя ключами, как в нашем Proof-of-Concept, это будет файл динамического маршрута [Pokemon]:
export async function getStaticPaths() { return { paths: [], fallback: 'blocking', } }
-
paths
— этоarray
, содержащий все пути, соответствующие этому маршруту, который будет предварительно обработан. -
fallback
имеет 3 возможных значения: blocking,true
илиfalse
В нашем случае наш getStaticPaths
определяет:
- Никакие пути не будут предварительно визуализированы;
- Всякий раз, когда вызывается этот маршрут, мы не будем обслуживать резервный шаблон, мы будем отображать страницу по запросу и заставлять пользователя ждать, блокируя выполнение других действий приложением.
При использовании On-Demand Builders убедитесь, что ваша резервная стратегия соответствует целям вашего приложения, официальная документация Next.js: резервная документация очень полезна.
До появления On-Demand Builders наш getStaticPaths
немного отличался:
export async function getStaticPaths() { const { pokemons } = await fetchPkmList() return { paths: pokemons.map(({ name }) => ({ params: { pokemon: name } })), fallback: false, } }
Мы собирали список всех страниц покемонов, которые мы собирались иметь, сопоставляли все объекты pokemon
просто со string
с именем покемона и перенаправляли возвращаемый объект { params }
, несущий его, в getStaticProps
. Для нашего fallback
было установлено значение false
, потому что, если маршрут не совпадал, мы хотели, чтобы Next.js выдавал страницу 404: Not Found
.
Вы можете проверить обе версии, развернутые в Netlify:
- С On-Demand Builder: код, работа
- Полностью статическая генерация: код, живой
Код также находится в открытом доступе на Github, и вы можете легко развернуть его самостоятельно, чтобы проверить время сборки. И с этой очередью мы скатываемся к нашей следующей теме.
Время сборки
Как упоминалось выше, предыдущая демонстрация на самом деле является Proof-of-Concept , нет ничего действительно хорошего или плохого, если мы не можем измерить. Для нашего небольшого исследования я перешел к PokeAPI и решил поймать всех покемонов.
В целях воспроизводимости я ограничил наш запрос (до 1000
). На самом деле это не все в API, но он обеспечивает, чтобы количество страниц было одинаковым для всех сборок, независимо от того, обновляются ли вещи в любой момент времени.
export const fetchPkmList = async () => { const resp = await fetch(`${API}pokemon?limit=${LIMIT}`) const { count, results, }: { count: number results: { name: string url: string }[] } = await resp.json() return { count, pokemons: results, limit: LIMIT, } }
А затем запустил обе версии в отдельных ветках в Netlify, благодаря предварительным развертываниям они могут сосуществовать практически в одной среде. Чтобы действительно оценить разницу между обоими методами, подход ODB был экстремальным, для этого динамического маршрута не было предварительно отрисовано ни одной страницы . Хотя это и не рекомендуется для реальных сценариев (вы захотите предварительно отобразить свои маршруты с интенсивным трафиком), он четко указывает диапазон улучшения производительности во время сборки, которого мы можем достичь с помощью этого подхода.
Стратегия | Число страниц | Количество активов | Время сборки | Общее время развертывания |
---|---|---|---|---|
Полностью статическая генерация | 1002 | 1005 | 2 минуты 32 секунды | 4 минуты 15 секунд |
Строители по требованию | 2 | 0 | 52 секунды | 52 секунды |
Страницы в нашем маленьком приложении PokeDex довольно маленькие, активы изображений очень скудны, но выигрыш во времени развертывания очень значителен. Если приложение имеет среднее или большое количество маршрутов, определенно стоит рассмотреть стратегию ODB.
Это делает ваши развертывания быстрее и, следовательно, более надежными. Снижение производительности происходит только при самом первом запросе, начиная с последующего запроса и далее визуализированная страница будет кэшироваться прямо на границе, что делает производительность точно такой же, как при полностью статической генерации.
Будущее: распределенный постоянный рендеринг
В тот же день были анонсированы и размещены в раннем доступе сборщики по требованию, Netlify также опубликовала свой запрос на комментарии к распределенному постоянному рендерингу (DPR).
DPR — это следующий шаг для сборщиков по требованию. Он извлекает выгоду из более быстрых сборок, используя такие асинхронные этапы сборки, а затем кэшируя активы до тех пор, пока они не будут фактически обновлены. Больше никаких полных сборок для веб-сайта размером 10 000 страниц. DPR дает разработчикам полный контроль над сборкой и развертыванием систем благодаря надежному кэшированию и использованию сборщиков по требованию.
Представьте себе такой сценарий: веб-сайт электронной коммерции имеет 10 000 страниц продуктов, это означает, что создание всего приложения для развертывания займет около 2 часов. Нам не нужно спорить, насколько это болезненно.
С помощью DPR мы можем установить 500 лучших страниц для создания при каждом развертывании. Наши страницы с самым большим трафиком всегда готовы для наших пользователей. Но, мы магазин, т.е. каждая секунда на счету. Таким образом, для остальных 9500 страниц мы можем установить хук после сборки для запуска их сборщиков — асинхронное развертывание оставшихся наших страниц и немедленное кэширование. Ни один пользователь не пострадал, наш сайт был обновлен максимально быстрой сборкой, а все остальное, чего не было в кеше, затем было сохранено.
Заключение
Хотя многие пункты обсуждения в этой статье были концептуальными, а реализация должна быть определена, я с нетерпением жду будущего Jamstack. Достижения, которые мы делаем как сообщество, вращаются вокруг опыта конечного пользователя.
Что вы думаете о распределенном постоянном рендеринге? Пробовали ли вы использовать конструкторы по требованию в своем приложении? Дайте мне знать больше в комментариях или позвоните мне в Twitter. Мне действительно любопытно!
использованная литература
- «Полное руководство по добавочной статической регенерации (ISR) с помощью Next.js», Ли Робинсон.
- «Более быстрая сборка больших сайтов на Netlify с помощью сборщиков по требованию», Асавари Тайал, блог Netlify
- «Распределенный постоянный рендеринг: новый подход Jamstack для более быстрой сборки», Мэтт Бильманн, блог Netlify.
- «Распределенный постоянный рендеринг (DPR)», Кэссиди Уильямс, GitHub.