Jamstack: философия веб-разработки на примере Next.js
Сперва JAMstack считался лишь комбинацией JavaScript, API и MarkUp. Теперь веб-разработка Jamstack превратилась в настоящую философию.
На этапе планирования сайта Академии IBS Dunice мы обратили внимание на недавно появившийся в мировом IT-сообществе принципиально иной способ создания сайтов под названием Jamstack.
На тот момент мы слышали много положительных отзывов об этом стеке (все они впоследствии подтвердились), и он уже был достаточно стабильным, чтобы взять его в работу. Как результат, этот стек позволил быстро сделать кастомный проект (не нужно создавать отдельный сервер, заниматься вопросами CD и т. д.). Но давайте по порядку.

На заре своего становления JAMstack считался лишь комбинацией JavaScript, API и MarkUp. Но сегодня это не так. Netlify, компания-создатель Jamstack, отказалась от сухой аббревиатуры JAM и сделала из Jamstack настоящую философию веб-разработки.
Что такое Jamstack
Jamstack сегодня можно определить как современную архитектуру веб-разработки, которая позволяет создавать веб-сайты на основе методологии микросервисов. Она даёт все преимущества статических страниц: высокую производительность и трафикоустойчивость, высокую скорость загрузки, безопасность, отличные показатели SEO и т. д.
Лучше понять, что такое Jamstack, можно на примере её отличий в рабочем процессе. На графике очень упрощённо, но тем не менее наглядно изображено, что происходит под капотом.
Сегодня существует два традиционных подхода: MPA и SPA. При этом Jamstack содержит все плюсы MPA и SPA и не содержит ни одного из их минусов. На изображении ниже плюсы отмечены зелёным, минусы — красным.
Почему Jamstack
Архитектура Jamstack имеет множество преимуществ вне зависимости от того, создаёте ли вы масштабный продукт электронной коммерции, SaaS-приложение или, как мы, корпоративный сайт. Ниже мы собрали лишь некоторые из её ключевых достоинств.
Высокая производительность
Всем известно, что скорость загрузки страниц напрямую влияет на пользовательский опыт и конверсию. С Jamstack страницы генерируются заранее в процессе сборки, что позволяет им загружаться быстрее.
А когда речь идёт об улучшении показателя TTFB (Time to First Byte), нет ничего лучше, чем предварительно созданные файлы, передаваемые через CDN.
Поэтому, учитывая, что все страницы уже предварительно загружены и доступны пользователю посредством CDN, можно добиться очень высокой производительности без внедрения дорогостоящей или сверхсложной инфраструктуры.
Повышенная безопасность
Jamstack освобождает инфраструктуру хостинга от многочисленных подвижных элементов, что уменьшает количество серверов и систем, нуждающихся в защите. Когда страницы и компоненты предварительно генерируются, хостинг нужен только для чтения, а это также снижает риски атак.
Например, при использовании mysql/mongodb, apache/nginx, RoR/Node/PHP, …, уязвимости могут быть в любом из этих звеньев.
Простое и дешёвое масштабирование
Многие архитектуры справляются с высокой нагрузкой, дополнительно добавляя логику для кэширования особо посещаемых страниц и ресурсов. В Jamstack эта функция работает по умолчанию благодаря SSG и CDN.
FAQ / Возможные проблемы (спойлер: проблемы, чаще всего, надуманные)
1. Если есть предварительный рендеринг, то как быть с обновлениями?
По умолчанию придётся пересобирать ваш сайт или приложение при каждом обновлении любого предварительно отрисованного на этапе сборки элемента. К примеру, чтобы создать новую запись в блоге, придётся собрать и задеплоить сайт заново. Однако с помощью вызовов API можно создавать динамические элементы.
Не стоит забывать, что всё ещё можно использовать JavaScript на фронте. Также существуют DPR/ISR/DSR.
2. Jamstack предполагает git-ориентированный рабочий процесс. Но я не хочу, чтобы пользователи имели доступ к репозиторию. Ни в каком виде. Лучше я воспользуюсь WordPress.
Можно где-то разместить свой отдельный сервер WordPress (или любую другую Headless-совместимую CMS), а у этих CMS есть JSON API. Этот API можно использовать или на этапе полной (или частичной) сборки, или же во время runtime (посредством вызовов API).
Почему Next.js
Самым популярным Jamstack-фреймворком, по данным jamstack.org, является Next.js. В общем рейтинге всех фреймворков он также занимает высокое место:
Но ещё пару лет назад поверить в эту статистику было бы сложно, ведь когда Next.js только появился на сцене, он был не очень совместим с архитектурой Jamstack, а именно — с предпочтительным в её философии методом статической генерации сайта (SSG).
Дело в том, что одним из основных принципов работы Next.js является рендеринг на стороне сервера (SSR), и эти два подхода к созданию приложений — SSG и SSR, казалось бы, противоположны, и их невозможно объединить.
Но в 2020 году в версии Next.js 9.3 появилась улучшенная поддержка SSG, а в 2021-м появилась поддержка гибридной модели, при которой можно пользоваться одновременно и в равной степени свободно как SSG, так и SSR. Поэтому Next.js теперь не такой уж и плохой выбор для архитектуры Jamstack, а даже наоборот — очень популярный.
Несмотря на огромное многообразие выбора платформ для создания своего сайта или приложения на архитектуре Jamstack, мы считаем Next.js поистине уникальным фреймворком. Почему?
1. Он одновременно поддерживает:
- (Server/SSR) рендеринг на стороне сервера во время выполнения (по аналогии с MPA);
- (Static) автоматический рендеринг в виде статического HTML;
- (SSG) автоматическую генерацию в виде статического HTML + JSON;
- (ISR) инкрементную статическую регенерацию.
2. Из коробки в Next.js есть:
- CI/CD с live previews;
- долгожданный и революционный встроенный компонент Image;
- роутинг страниц (с фоновой предзагрузкой содержимого внутренних ссылок — в формате JSON «под капотом»);
- serverless;
- автообновляемые SSL-сертификаты (https);
- i18n;
- http/2;
- CDN-кэширование и ревалидация;
- s(css)-модули.
3. Сравнивая Next.js с его основным конкурентом Nuxt.js, следует отметить, что:
- согласно npmtrends.com, популярность Next.js растет значительно быстрее;
- у Next.js более чем в 2 раза больше звезд на Github;
- с Next.js можно использовать Vercel, который подходит для него лучше, чем универсальные платформы для статических генераторов.
Метрики качества веб-сайта на примере работы с Next.js
В этой части мы расскажем, как можно улучшить показатели веб-сайта на примере опыта работы над сайтом Академии IBS Dunice edu.dunice.net. Сайт разрабатывался на Next.js и Vercel, поэтому преимущества данного стека можно будет наблюдать на практике.
Стоит начать с того, какие вообще бывают метрики качества веб-сайта/приложения. Существует несколько основных категорий метрик:
- Performance
- SEO
- Accessibility
- Best practices
- PWA
Фактически здесь они расположены по убыванию приоритета. Далее речь пойдёт о метрике Performance, потому как она самая важная, а также потому, что остальные метрики технически оптимизируются проще и однозначнее.
Высокие показатели Performance сегодня жизненно необходимы каждому сайту, ведь пользователи готовы тратить на ожидание загрузки страниц всё меньше и меньше времени.
Так, Google недавно включил скорость загрузки сайта в число наиболее важных из ключевых метрик, а по некоторым данным, более 50% мобильных пользователей покидают веб-сайты, если их загрузка длится дольше трёх секунд.
Поэтому сегодня медленная загрузка страниц может негативно сказаться и на конверсии покупок, и на лояльности к бренду, и на ряде других показателей.
Метрика Performance состоит:
- из основных метрик (по версии/инициативе Google)
LCP (Largest Contentful Paint)
FID (First Input Delay)
CLS (Cumulative Layout Shift)
- дополнительных метрик
TTFB (Time To First Byte)
FP (First Paint)
FCP (First Contentful Paint)
TBT (Total Blocking Time)
TTI (Time to Interactive)
- других метрик (не на все из них можно как-то повлиять)
RTT (Round Trip Time)
SSR on/off (Server Side Rendering) — опциональный этап, зависит от выбираемой архитектуры
F-CR (Fetching / Computing & Rendering) — аббревиатура, созданная исключительно в рамках данного текста
Зависимость метрик имеет следующий вид:
SSR → RTT → TTFB → F-CR {FP → FCP → LCP → остаточный TBT → FID (~=TTI)}
Измеряют метрики с помощью таких инструментов, как Chrome Dev Tool, web.dev/measure, Web Vitals. PageSpeed Insights непосредственно измеряет метрику Performance. Есть ещё Webpack bundle analyzer: для Next.js — @next/bundle-analyzer.
Высокие результаты метрик сайта edu.dunice.net на web.dev/measure видны на этом скриншоте:
Далее мы дадим практические советы по улучшению показателей Performance, как раз основываясь на опыте работы над сайтом edu.dunice.net.
Пометка NextVercel в последующих пунктах будет означать, что то или иное действие в связке Next и Vercel выполняется автоматически.
TTFB (Time To First Byte)
- Сократить RTT за счёт распределённого хостинга, чтобы ответы на запросы браузера приходили от ближайшего сервера (NextVercel: CDN).
- Исключить (или закэшировать результаты) SSR для целевых страниц (NextVercel: Static & SSG pages and Edge Functions).
Fetching (download time only)
- Исключить или сократить переадресации (redirects).
- Минифицировать JS и CSS (NextVercel: out of the box by Webpack).
- Включить gzip и http/2 на стороне контент-сервера (NextVercel: CDN).
- Оптимизировать разрешение, качество и формат (webp или avif)* всех изображений. Это даёт максимальный прирост. Также стоит задать приоритет изображениям, отображаемым при загрузке (NextVercel:
component + есть встроенная поддержка Low Quality Image Placeholders (LQIP)).*Нужно учитывать, что не все браузеры поддерживают данные форматы, и генерировать и доставлять нужные форматы для всех изображений автоматически — это сложная задача. Next.js всё это поддерживает с 11-й версии.
F-CR (Fetching + Computing & Rendering)
- Исключить или сократить Client Side Rendering (CSR) по крайней мере той части контента, которая соответствует LCP (NextVercel: render at build time ~ Jamstack).
- Отложить загрузку и вычисление* частей контента (в том числе и «тяжёлых» библиотек-зависимостей), которые не требуются для LCP. Для этого необходимо использовать Code Splitting (разделение кода) и Lazy loading (ленивую загрузку) (NextVercel: Dynamic Import).
Здесь можно найти пример кода.
* Имеется в виду, что движок V8 затрачивает время на выполнение JS и это время влияет на все метрики после TTFB.Также очень сильно влияет, по крайней мере на синтетические метрики, загрузка аналитик типа Google Analytics и Yandex Webmaster, поэтому рекомендуется отложить их загрузку* до наступления одного (того, что наступит раньше — race) из условий:
1) прошло несколько секунд (время заведомо большее, чем LCP/TTI);
2) пользователь совершил какое-либо действие (нажал любую кнопку, сдвинул курсор мыши, изменил ориентацию landscape/tablet).Пример кода здесь.
*— Если сделать это правильно, то аналитики будут работать корректно, а боты поисковиков, которые отслеживают скорость загрузки страниц, не будут вызывать никаких событий пользовательского ввода (при том прирост скорости загрузки будет не только синтетическим, но и фактическим — для реального пользователя).— Не рекомендуется делать подмену контента на основании заголовка запроса User Agent на сервере с целью отдачи поисковикам содержимого страниц без подключенных аналитик. Также можно использовать (s)css modules, что, кроме прочего, сократит размер загружаемых стилей (как минимум до момента LCP).Если все стили будут в одном файле, это повлияет на время начала интерпретации стилей контента, видимого при загрузке (scroll position 0). (NextVercel: out of the box). Critical (render-blocking) css можно выделить отдельно и загружать раньше остальных стилей, включив содержимое этих стилей непосредственно внутрь тега в , но это увеличит размер первоначального html, так что здесь тоже необходим баланс.
- Оптимизировать цепочки критических запросов (пока не подгружен один файл, не начнёт загружаться другой) до стадии TTI. Это можно отследить на вкладках Performance и Lighthouse в Chrome Dev Tools:— В первую очередь не стоит перегружать html-страницу, которую клиент получает в результате самого первого своего запроса, так как этот html содержит ссылки на все остальные ассеты (шрифты, скрипты, стили), которые должны начать загружаться как можно скорее.— Применяйте (где необходимо) атрибуты preconnect / prefetch / preload и async / defer у блокирующих скриптов (NextVercel: Script); для выявления неиспользуемого кода используйте Coverage в Chrome Dev Tools.
- В целом не должно быть asset’ов слишком маленького размера (исключение: блокирующие asset’ы). Это обусловлено тем, что время на полную обработку микро asset’а включает в себя время на TTFB. Но и не стоит всё «складывать в одну корзину» — здесь нужен баланс. Сложно сказать наверняка, какое суммарное количество запросов будет оптимальным (тем более в общем случае), но стоит ориентироваться на пределы до 50–100 запросов.
- Логотипы и другие небольшие изображения, видимые при загрузке страницы, можно загружать в формате Base64.
- Оптимизировать загрузку шрифтов.
Надеемся, вы заметили, что большинство вышеперечисленных методов содержат пометку NextVercel. В сущности, это лучшая иллюстрация универсальности и ценности архитектуры Jamstack, ведь то, что в других решениях пришлось бы делать «руками», в Jamstack встроено по умолчанию.
Взяв за основу собственный опыт, мы очень хотели продемонстрировать все (или почти все) преимущества архитектуры Jamstack на примере Next.js. Работа с этим стеком оказалась весьма интересной, т. к. он даёт возможность решать самые разные задачи и отвечает всем современным запросам.
Мы убеждены, что наши рекомендации сработают эффективно и на ваших проектах. В любом случае будем рады обратной связи.