Год назад во фреймворке Next.js 9.3 появилась поддержка генерирования статических сайтов (Static Site Generation, SSG), что сделало его первым гибридным фреймворком. Я к тому моменту уже несколько лет с удовольствием пользовался Next.js. Но тот релиз сделал Next.js моим новым стандартным инструментом. После того, как я много и серьёзно поработал с Next.js, я присоединился к Vercel для того чтобы помогать компаниям, вроде Tripadvisor и Washington Post, в деле внедрения Next.js и расширения того, что у них получилось.
В этом материале мне хотелось бы исследовать новый виток эволюции Jamstack — механизм инкрементной регенерации статических сайтов (Incremental Static Regeneration, ISR). Здесь вы найдёте руководство по ISR, а так же — практические примеры использования этой технологии, демонстрационные проекты и рассказ о сопутствующих внедрению ISR компромиссах.
Если в двух словах описать ISR, то окажется, что эта технология позволяет, при внесении каких-то изменений в материалы сайта, мгновенно обновлять статический контент. Полная пересборка проекта при этом не нужна. Гибридный подход Next.js позволяет использовать ISR в сфере электронной коммерции, при подготовке маркетинговых и рекламных страниц, при организации работы блогов и во многих других случаях.
В основе Jamstack лежит привлекательная идея: сайты представляют собой заранее отрендеренные статические страницы, которые можно отправить на CDN, и с которыми, через считанные секунды после этого, смогут работать пользователи со всего мира. Статические страницы быстры, статические сайты устойчивы к сбоям, их очень быстро индексируют поисковые роботы. Но и тут имеются некоторые проблемы.
Если архитектура Jamstack используется при разработке крупномасштабного статического сайта, это значит, что создателям сайта, возможно, придётся тратить долгие часы на его сборку. Если количество страниц сайта удвоится — удвоится и время сборки. Взглянем, например, на Target.com. Реально ли сгенерировать миллионы статических страниц товаров при каждом развёртывании сайта?
Проблема генерирования статических сайтов: так как время сборки линейно зависит от количества страниц — сборка сайта может занять многие часы
Даже если каждая статическая страница, что нереально, будет сгенерирована за 1 мс, на пересборку всего сайта, всё равно, уйдёт несколько часов. В случае с большими веб-приложениями использование SSG для всех материалов таких приложений — это крайне неудачная затея. Команды, работающие над крупными проектами, нуждаются в более гибких гибридных решениях, учитывающих индивидуальные особенности таких проектов.
При работе над многими веб-проектами практикуется отделение контента этих проектов от их кода. Использование систем управления контентом (Content Management System, CMS) без пользовательского интерфейса позволяет редакторам сайтов публиковать новые и изменённые материалы, не привлекая к решению этой задачи программистов. Но если речь идёт о традиционных статических сайтах, то этот процесс может быть довольно медленным.
Представим себе интернет-магазин, в котором имеется 100000 товаров. Их цены часто меняются. Когда редактор меняет, в рамках акции, цену наушников с $100 на $75, применяемая на сайте CMS задействует веб-хук для запуска пересборки всего сайта. Нереально ждать многие часы того момента, когда новая цена появится на сайте.
Длительные процессы сборки сайтов, в ходе которых выполняются вычисления, в которых нет необходимости, могут приводить к дополнительным расходам. В идеале приложение должно быть достаточно интеллектуальным для того чтобы понимать то, данные какого именно товара изменены, и инкрементально, не испытывая необходимости в полной пересборке сайта, обновлять соответствующие страницы.
Next.js позволяет создавать или обновлять статические страницы после того, как выполнена сборка сайта. Инкрементная регенерация статических сайтов позволят разработчикам и редакторам использовать механизмы генерирования статических сайтов в применении к отдельным страницам, без необходимости пересобирать весь сайт. Применение ISR позволяет сохранить сильные стороны SSG в масштабах проектов, состоящих из миллионов страниц.
Благодаря использованию ISR статические страницы могут быть сгенерированы во время работы проекта (по запросу), а не во время его сборки. Используя аналитические данные, A/B-тестирование, или другие метрики, разработчик, видя плюсы и минусы ISR и SSG, сознательно идя на определённые компромиссы, получает возможность гибкой настройки процессов сборки проекта.
Вспомним вышеприведённый пример интернет-магазина, в котором имеется 100000 товаров. Если на генерирование страницы для одного товара уйдёт 50 мс, что вполне реально, то без использования ISR на сборку всего сайта понадобится почти 2 часа. Других вариантов тут нет. А вот если применяется ISR — у разработчика сайта появляется возможность выбора одного из сценариев сборки:
Преимущества ISR: у разработчика есть возможность выбора стратегии генерирования страниц во время сборки проекта. Вариант A позволяет ускорить сборки, вариант B позволяет кешировать больше готовых страниц
Теперь давайте подробнее рассмотрим наш пример использования ISR в интернет-магазине.
Если вы не пользовались раньше Next.js, то я порекомендовал бы почитать этот материал для того чтобы разобраться с основами. ISR использует тот же API, который применяется при генерировании статических сайтов — getStaticProps. Мы, устанавливая параметр
Последовательность запросов, используемых при реализации ISR
Next.js позволяет настраивать то, какие страницы товаров нужно генерировать во время сборки проекта, а какие — по запросу. Сгенерируем во время сборки проекта лишь страницы для 1000 самых популярных товаров, передав системе в
Нам нужно настроить поведение системы в ситуации, когда кто-то запрашивает страницу продукта, которая не была сгенерирована в ходе первоначальной сборки проекта. Для этого используется параметр
Конечный пользователь — это основной объект внимания Next.js. Понятие «лучший способ работы со статическими страницами» относительно, его смысл зависит от отрасли экономики, к которой принадлежит проект, от его аудитории, от внутренних особенностей приложения. Next.js позволяет разработчикам реализовывать разные стратегии работы со статическим контентом и при этом не покидать границ фреймворка. Благодаря этому, используя Next.js, можно подобрать именно то, что лучше всего подходит для конкретного проекта.
ISR — это не всегда именно то, что нужно некоему проекту. Например, лента новостей Facebook не может выводить устаревшие данные. В данном случае имеет смысл прибегнуть к серверному рендерингу (Server-Side Rendering, SSR) и, возможно, к использованию собственных заголовков
SSR и кеширование данных на пограничных системах напоминает ISR (особенно — при использовании заголовков
Обратите внимание на то, что использование SSR без кеширования может привести к ухудшению производительности проекта. Когда пользователь ждёт вывода страницы проекта — важна каждая миллисекунда. Использование SSR без кеширования, кроме того, может очень плохо сказаться на показателе TTFB (Time to First Byte, время до первого байта).
ISR не всегда имеет смысл применять на маленьких сайтах. Если период ревалидации контента больше, чем время, необходимое на пересборку всего сайта, то вместо ISR вполне можно использовать традиционный подход по генерированию статических сайтов.
Если React на сайте используется без Next.js — это значит, что речь идёт о клиентском рендеринге сайта (Client-Side Rendering, CSR). Приложение отдаёт посетителю страницу, пребывающую в некоем исходном состоянии, после чего, из JavaScript-кода, работающего на клиенте, выполняются запросы на загрузку дополнительных данных (например — с применением
Например, то, что в CSR-проектах нет страниц, заранее отрендеренных на основе исходной HTML-разметки, ухудшает возможности по SEO. Кроме того, CSR-страницы не будут работать при выключенном на клиенте JavaScript.
Если данные могут быть загружены очень быстро — имеет смысл присмотреться к параметру
Хотя я, говоря об ISR, всё время обсуждал кеширование, я не могу не отметить того факта, что эта технология спроектирована так, чтобы сохранять сгенерированные страницы между сеансами развёртывания проектов. Это значит, что у владельца проекта есть возможность мгновенного отката на его предыдущую версию и возможность не терять ранее сгенерированные страницы.
Каждому развёртыванию можно назначить ключ, представляющий собой некий идентификатор (ID). Этот ID Next.js использует для организации постоянного хранения сгенерированных страниц. При откате проекта можно поменять ключ так, чтобы он указывал бы на предыдущее развёртывание, что позволяет выполнять атомарные развёртывания проектов. Это значит, что можно заглянуть на предыдущий иммутабельный вариант развёрнутого проекта, и то, что он будет работать так, как ожидается. Вот пример возврата к предыдущей версии кода с помощью ISR:
Возможность отката к предыдущим развёртываниям сайта и организация постоянного хранения статических страниц не входят в сферу ответственности Next.js. Они зависят от провайдера. Обратите внимание на то, что ISR отличается от SSR с применением заголовков
Как уже было сказано, ISR находит успешное применение в самых разных проектах. Вот несколько примеров:
Самостоятельные разработчики и большие команды выбирают Next.js за то, что в этом фреймворке реализован гибридный подход к рендерингу страниц, и за то, что он позволяет инкрементно генерировать страницы по мере возникновения необходимости в них. В распоряжении того, кто пользуется ISR, появляются возможности генератора статических сайтов, объединённые с гибкостью серверного рендеринга. Для того чтобы создать проект, в котором можно воспользоваться ISR, нет нужды в каких-то особых настройках. Достаточно выполнить команду
Фреймворк Next.js был спроектирован в расчёте на его постепенное внедрение в веб-проекты. Применяя Next.js можно продолжить пользоваться существующим кодом, и при этом добавить в проект именно столько React-кода, сколько нужно. Начиная с малого и инкрементально добавляя в проект страницы, создаваемые с помощью Next.js, можно избежать отказа от неких возможностей сайта, вызванного тем, что код, реализующий эти возможности, нужно полностью переписывать. В общем — знакомьтесь с Next.js и с удовольствием пишите код своих проектов.
Пользуетесь ли вы ISR?
В этом материале мне хотелось бы исследовать новый виток эволюции Jamstack — механизм инкрементной регенерации статических сайтов (Incremental Static Regeneration, ISR). Здесь вы найдёте руководство по ISR, а так же — практические примеры использования этой технологии, демонстрационные проекты и рассказ о сопутствующих внедрению ISR компромиссах.
Если в двух словах описать ISR, то окажется, что эта технология позволяет, при внесении каких-то изменений в материалы сайта, мгновенно обновлять статический контент. Полная пересборка проекта при этом не нужна. Гибридный подход Next.js позволяет использовать ISR в сфере электронной коммерции, при подготовке маркетинговых и рекламных страниц, при организации работы блогов и во многих других случаях.
Проблема SSG
В основе Jamstack лежит привлекательная идея: сайты представляют собой заранее отрендеренные статические страницы, которые можно отправить на CDN, и с которыми, через считанные секунды после этого, смогут работать пользователи со всего мира. Статические страницы быстры, статические сайты устойчивы к сбоям, их очень быстро индексируют поисковые роботы. Но и тут имеются некоторые проблемы.
Если архитектура Jamstack используется при разработке крупномасштабного статического сайта, это значит, что создателям сайта, возможно, придётся тратить долгие часы на его сборку. Если количество страниц сайта удвоится — удвоится и время сборки. Взглянем, например, на Target.com. Реально ли сгенерировать миллионы статических страниц товаров при каждом развёртывании сайта?
Проблема генерирования статических сайтов: так как время сборки линейно зависит от количества страниц — сборка сайта может занять многие часы
Даже если каждая статическая страница, что нереально, будет сгенерирована за 1 мс, на пересборку всего сайта, всё равно, уйдёт несколько часов. В случае с большими веб-приложениями использование SSG для всех материалов таких приложений — это крайне неудачная затея. Команды, работающие над крупными проектами, нуждаются в более гибких гибридных решениях, учитывающих индивидуальные особенности таких проектов.
Системы управления контентом
При работе над многими веб-проектами практикуется отделение контента этих проектов от их кода. Использование систем управления контентом (Content Management System, CMS) без пользовательского интерфейса позволяет редакторам сайтов публиковать новые и изменённые материалы, не привлекая к решению этой задачи программистов. Но если речь идёт о традиционных статических сайтах, то этот процесс может быть довольно медленным.
Представим себе интернет-магазин, в котором имеется 100000 товаров. Их цены часто меняются. Когда редактор меняет, в рамках акции, цену наушников с $100 на $75, применяемая на сайте CMS задействует веб-хук для запуска пересборки всего сайта. Нереально ждать многие часы того момента, когда новая цена появится на сайте.
Длительные процессы сборки сайтов, в ходе которых выполняются вычисления, в которых нет необходимости, могут приводить к дополнительным расходам. В идеале приложение должно быть достаточно интеллектуальным для того чтобы понимать то, данные какого именно товара изменены, и инкрементально, не испытывая необходимости в полной пересборке сайта, обновлять соответствующие страницы.
ISR
Next.js позволяет создавать или обновлять статические страницы после того, как выполнена сборка сайта. Инкрементная регенерация статических сайтов позволят разработчикам и редакторам использовать механизмы генерирования статических сайтов в применении к отдельным страницам, без необходимости пересобирать весь сайт. Применение ISR позволяет сохранить сильные стороны SSG в масштабах проектов, состоящих из миллионов страниц.
Благодаря использованию ISR статические страницы могут быть сгенерированы во время работы проекта (по запросу), а не во время его сборки. Используя аналитические данные, A/B-тестирование, или другие метрики, разработчик, видя плюсы и минусы ISR и SSG, сознательно идя на определённые компромиссы, получает возможность гибкой настройки процессов сборки проекта.
Вспомним вышеприведённый пример интернет-магазина, в котором имеется 100000 товаров. Если на генерирование страницы для одного товара уйдёт 50 мс, что вполне реально, то без использования ISR на сборку всего сайта понадобится почти 2 часа. Других вариантов тут нет. А вот если применяется ISR — у разработчика сайта появляется возможность выбора одного из сценариев сборки:
- Ускорение сборки. Во время сборки проекта генерируются страницы для 1000 самых популярных товаров. Запросы, выполненные к страницам других товаров, рассматриваются как промахи кеша. Запрошенные страницы, статические, генерируются по запросу. Речь идёт о сборках длительностью в 1 минуту.
- Повышение частоты попаданий в кеш. Во время сборки проекта генерируется 10000 страниц, что позволяет обеспечить кеширование большего числа страниц товаров до поступления запросов на их загрузку. Этот вариант предусматривает выполнение сборок длительностью 8 минут.
Преимущества ISR: у разработчика есть возможность выбора стратегии генерирования страниц во время сборки проекта. Вариант A позволяет ускорить сборки, вариант B позволяет кешировать больше готовых страниц
Теперь давайте подробнее рассмотрим наш пример использования ISR в интернет-магазине.
Разбор примера
?Загрузка данных
Если вы не пользовались раньше Next.js, то я порекомендовал бы почитать этот материал для того чтобы разобраться с основами. ISR использует тот же API, который применяется при генерировании статических сайтов — getStaticProps. Мы, устанавливая параметр
revalidate
в значение 60
, сообщаем Next.js о том, что для обрабатываемой страницы нужно применять ISR.Последовательность запросов, используемых при реализации ISR
- Next.js позволяет указать время повторной валидации для каждой страницы. Установим его в 60 секунд.
- Первый запрос к странице товара приведёт к передаче клиенту кешированной страницы с исходной ценой товара.
- В данные товара, хранящиеся в CMS, вносятся изменения.
- Запросы, выполняемые к странице после первого запроса и до истечения 60 секунд, обслуживаются из кеша, ответы на них отдают клиенту мгновенно.
- Если по истечении интервала в 60 секунд поступает запрос на загрузку той же страницы — в ответ на него выдаётся её кешированная (устаревшая) версия. Next.js запускает фоновый процесс регенерации страницы.
- После того, как страница будет успешно сгенерирована, Next.js инвалидирует кеш и в ответ на запросы к этой странице выдаётся её обновлённый вариант. Если фоновая регенерация страницы не удалась — в кеше остаётся старая неизменённая страница.
// pages/products/[id].js
export async function getStaticProps({ params }) {
return {
props: {
product: await getProductFromDatabase(params.id)
},
revalidate: 60
}
}
?Генерирование путей
Next.js позволяет настраивать то, какие страницы товаров нужно генерировать во время сборки проекта, а какие — по запросу. Сгенерируем во время сборки проекта лишь страницы для 1000 самых популярных товаров, передав системе в
getStaticPath
список идентификаторов соответствующих товаров.Нам нужно настроить поведение системы в ситуации, когда кто-то запрашивает страницу продукта, которая не была сгенерирована в ходе первоначальной сборки проекта. Для этого используется параметр
fallback
, который может принимать значения blocking
и true
.fallback: blocking
(рекомендуется использовать именно это значение). Когда возникает необходимость в странице, которая ещё не сгенерирована, Next.js выполнит серверный рендеринг этой страницы по первому запросу. При обработке следующих запросов к этой странице в ответ на них из кеша будет выдаваться статический файл.fallback: true
. Когда возникает необходимость в ещё не сгенерированной странице — Next.js немедленно, по первому запросу, возвратит статическую страницу с индикатором загрузки. Когда завершится загрузка данных — будет выполнен повторный рендеринг этой страницы, новые данные будут помещены в кеш. При обслуживании следующих запросов к той же странице будет использован статический файл из кеша.
// pages/products/[id].js
export async function getStaticPaths() {
const products = await getTop1000Products()
const paths = products.map((product) => ({
params: { id: product.id }
}))
return { paths, fallback: ‘blocking’ }
}
Компромиссы
Конечный пользователь — это основной объект внимания Next.js. Понятие «лучший способ работы со статическими страницами» относительно, его смысл зависит от отрасли экономики, к которой принадлежит проект, от его аудитории, от внутренних особенностей приложения. Next.js позволяет разработчикам реализовывать разные стратегии работы со статическим контентом и при этом не покидать границ фреймворка. Благодаря этому, используя Next.js, можно подобрать именно то, что лучше всего подходит для конкретного проекта.
?Серверный рендеринг
ISR — это не всегда именно то, что нужно некоему проекту. Например, лента новостей Facebook не может выводить устаревшие данные. В данном случае имеет смысл прибегнуть к серверному рендерингу (Server-Side Rendering, SSR) и, возможно, к использованию собственных заголовков
Cache-Control
с суррогатными ключами для инвалидации содержимого различных кешей. Так как Next.js — это гибридный фреймворк — у разработчика есть возможность пойти на компромисс, связанный с использованием SSR, но при этом не покидать границ фреймворка.// Страницы, отрендеренные на сервере, можно кешировать на пограничных серверах
// при этом Next.js можно пользоваться и в getServerSideProps и в API Routes
res.setHeader('Cache-Control', 's-maxage=60, stale-while-revalidate');
SSR и кеширование данных на пограничных системах напоминает ISR (особенно — при использовании заголовков
stale-while-revalidate
для управления кешем). Основная разница между ними заключается в том, как именно обрабатывается первый запрос. При использовании ISR можно сделать так, чтобы в ответ на первый запрос к некоей странице, при условии её предварительного рендеринга, гарантированно выдавался бы её статический вариант. Даже если база данных проекта вдруг оказалась недоступной, или если возникли проблемы, относящиеся к взаимодействию с API, пользователь проекта, всё равно, увидит правильную статическую страницу. Но SSR позволяет настраивать страницы, ориентируясь на особенности входящих запросов.Обратите внимание на то, что использование SSR без кеширования может привести к ухудшению производительности проекта. Когда пользователь ждёт вывода страницы проекта — важна каждая миллисекунда. Использование SSR без кеширования, кроме того, может очень плохо сказаться на показателе TTFB (Time to First Byte, время до первого байта).
?Генерирование статических сайтов
ISR не всегда имеет смысл применять на маленьких сайтах. Если период ревалидации контента больше, чем время, необходимое на пересборку всего сайта, то вместо ISR вполне можно использовать традиционный подход по генерированию статических сайтов.
?Клиентский рендеринг
Если React на сайте используется без Next.js — это значит, что речь идёт о клиентском рендеринге сайта (Client-Side Rendering, CSR). Приложение отдаёт посетителю страницу, пребывающую в некоем исходном состоянии, после чего, из JavaScript-кода, работающего на клиенте, выполняются запросы на загрузку дополнительных данных (например — с применением
useEffect
). Хотя это и расширяет возможности по хостингу сайтов (так как при таком подходе нет абсолютной необходимости в сервере приложения), у такого подхода есть и свои недостатки.Например, то, что в CSR-проектах нет страниц, заранее отрендеренных на основе исходной HTML-разметки, ухудшает возможности по SEO. Кроме того, CSR-страницы не будут работать при выключенном на клиенте JavaScript.
Особенности настройки параметра fallback при применении ISR
Если данные могут быть загружены очень быстро — имеет смысл присмотреться к параметру
fallback: blocking
. Тогда не придётся заботиться о показе клиенту страницы, предлагающей подождать, и при обращении к любой странице клиент всегда получит одно и то же (вне зависимости от того, кеширована ли эта страница). Если же данные загружаются медленно — применение fallback: true
позволяет немедленно показать пользователю временную страницу.ISR — это не только кеширование!
Хотя я, говоря об ISR, всё время обсуждал кеширование, я не могу не отметить того факта, что эта технология спроектирована так, чтобы сохранять сгенерированные страницы между сеансами развёртывания проектов. Это значит, что у владельца проекта есть возможность мгновенного отката на его предыдущую версию и возможность не терять ранее сгенерированные страницы.
Каждому развёртыванию можно назначить ключ, представляющий собой некий идентификатор (ID). Этот ID Next.js использует для организации постоянного хранения сгенерированных страниц. При откате проекта можно поменять ключ так, чтобы он указывал бы на предыдущее развёртывание, что позволяет выполнять атомарные развёртывания проектов. Это значит, что можно заглянуть на предыдущий иммутабельный вариант развёрнутого проекта, и то, что он будет работать так, как ожидается. Вот пример возврата к предыдущей версии кода с помощью ISR:
- В проект внесён новый код, развёртыванию назначен ID 123.
- Оказалось, что на одной из страниц имеется опечатка — «Smshng Magazine», а надо — «Smashing Magazine».
- Страницу редактируют в CMS, при этом для исправления ошибки не нужно выполнять повторное развёртывание проекта.
- После того, как на странице окажется нужный текст — «Smashing Magazine» — она, на постоянной основе, размещается в хранилище.
- Потом в проект внесён новый код, который содержит серьёзные ошибки, развёртывание получило ID 354.
- Было принято решение откатиться к релизу с ID 123.
- После этого на странице, где была опечатка, остался правильный текст — «Smashing Magazine».
Возможность отката к предыдущим развёртываниям сайта и организация постоянного хранения статических страниц не входят в сферу ответственности Next.js. Они зависят от провайдера. Обратите внимание на то, что ISR отличается от SSR с применением заголовков
Cache-Control
, так как кешированные данные, по своей природе, действительны лишь ограниченное время. Подобные кеши действительны лишь при работе с определённой версией сайта и при откате к его предыдущей версии очищаются.Примеры применения ISR
Как уже было сказано, ISR находит успешное применение в самых разных проектах. Вот несколько примеров:
- Интернет-магазин. Это — стартовый набор Next.js Commerce, который нацелен на создание высокопроизводительных сайтов, ориентированных на сферу электронной коммерции.
- Статическая страница, на которой собраны сведения о реакции пользователей на задачу из трекера задач GitHub. Эта страница регенерируется с использованием ISR.
- Статическая страница, на которой выводятся твиты, встроенные в страницу. То есть — показ этих твитов после формирования страницы, не зависит от внешних источников данных.
Сейчас — самое время изучить Next.js
Самостоятельные разработчики и большие команды выбирают Next.js за то, что в этом фреймворке реализован гибридный подход к рендерингу страниц, и за то, что он позволяет инкрементно генерировать страницы по мере возникновения необходимости в них. В распоряжении того, кто пользуется ISR, появляются возможности генератора статических сайтов, объединённые с гибкостью серверного рендеринга. Для того чтобы создать проект, в котором можно воспользоваться ISR, нет нужды в каких-то особых настройках. Достаточно выполнить команду
next start
.Фреймворк Next.js был спроектирован в расчёте на его постепенное внедрение в веб-проекты. Применяя Next.js можно продолжить пользоваться существующим кодом, и при этом добавить в проект именно столько React-кода, сколько нужно. Начиная с малого и инкрементально добавляя в проект страницы, создаваемые с помощью Next.js, можно избежать отказа от неких возможностей сайта, вызванного тем, что код, реализующий эти возможности, нужно полностью переписывать. В общем — знакомьтесь с Next.js и с удовольствием пишите код своих проектов.
Пользуетесь ли вы ISR?
artemu78
Скажи, а где считаются те самые 60 секунд до валидации? на клиенте или на сервере?
Если на сервере - то я могу сгенерированные страницы разнести по CDN ?
Вообще вся эта система с ревалидацией работает если разносить по CDN?
artemu78
Аа блин, это перевод
Спасибо за перевод, спасибо что сохранили ссылку на оригинал, пойду к автору
От себя добавлю - юзаю NextJS на втором проекте уже и это реально крутая штуковина.