Опубликовано: 6 февраля 2019 г., Последнее обновление: 5 января 2026 г.
Одно из ключевых решений, которое должны принять веб-разработчики, — это где реализовать логику и отрисовку в своем приложении. Это может быть сложно, поскольку существует множество способов создания веб-сайта.
Наше понимание этой области основано на нашей работе с Chrome, взаимодействующим с крупными сайтами в течение последних нескольких лет. В целом, мы рекомендуем разработчикам рассматривать серверный или статический рендеринг вместо подхода с полной гидратацией.
Для лучшего понимания архитектур, из которых мы выбираем при принятии этого решения, нам необходима согласованная терминология и общая структура для каждого подхода. Тогда вы сможете лучше оценить компромиссы каждого подхода к рендерингу с точки зрения производительности страницы.
Терминология
Для начала определим некоторые термины, которые будем использовать.
Визуализация
- Рендеринг на стороне сервера (SSR)
- Рендеринг приложения на сервере для отправки клиенту HTML-кода, а не JavaScript.
- Рендеринг на стороне клиента (CSR)
- Отображение приложения в браузере с использованием JavaScript для изменения DOM.
- Предварительная отрисовка
- Запуск клиентского приложения во время сборки для сохранения его начального состояния в виде статического HTML. Обратите внимание, что «предварительная отрисовка» в этом смысле отличается от предварительной отрисовки браузером будущих навигаций .
- Увлажнение
- Запуск клиентских скриптов для добавления состояния приложения и интерактивности к HTML-коду, генерируемому сервером. Гидратация предполагает, что DOM не изменяется.
- Регидратация
- Хотя термин «регидратация» часто используется в значении того же самого, что и «гидратация», он подразумевает регулярное обновление DOM с учетом последнего состояния, в том числе и после первоначальной гидратации.
Производительность
- Время до первого байта (TTFB)
- Время между нажатием на ссылку и загрузкой первого байта контента на новой странице.
- First Contentful Paint (FCP)
- Время, когда запрошенный контент (текст статьи и т. д.) становится видимым.
- Взаимодействие с Next Paint (INP)
- Репрезентативный показатель, оценивающий, насколько быстро страница реагирует на действия пользователя.
- Общее время блокировки (TBT)
- Показатель , используемый в качестве косвенного критерия для INP , который вычисляет, как долго основной поток был заблокирован во время загрузки страницы.
Рендеринг на стороне сервера
Рендеринг на стороне сервера генерирует полный HTML-код страницы на сервере в ответ на навигацию. Это позволяет избежать дополнительных запросов на получение данных и создание шаблонов на стороне клиента, поскольку рендерер обрабатывает их до того, как браузер получит ответ.
Рендеринг на стороне сервера обычно обеспечивает быструю загрузку страницы (FCP). Выполнение логики страницы и рендеринг на сервере позволяют избежать отправки большого количества JavaScript-кода на клиент. Это помогает сократить время загрузки страницы (TTBT), что также может привести к снижению времени отклика (INP), поскольку основной поток не блокируется так часто во время загрузки страницы. Когда основной поток блокируется реже, у взаимодействия с пользователем появляется больше возможностей для более быстрого выполнения.
Это логично, поскольку при рендеринге на стороне сервера вы фактически просто отправляете текст и ссылки в браузер пользователя. Такой подход хорошо работает в различных условиях устройств и сетей и открывает интересные возможности для оптимизации браузера, например, потоковую обработку документов.

При использовании серверного рендеринга пользователи с меньшей вероятностью будут ждать выполнения ресурсоемкого JavaScript-кода, прежде чем смогут воспользоваться вашим сайтом. Даже если вы не можете полностью избежать использования стороннего JavaScript , использование серверного рендеринга для снижения затрат на собственный JavaScript может освободить вам больше средств для остальных задач. Однако у этого подхода есть один потенциальный недостаток: генерация страниц на сервере занимает время, что может увеличить время отклика страницы (TTFB).
Достаточно ли серверного рендеринга для вашего приложения, во многом зависит от типа разрабатываемого вами интерфейса. Долгое время ведутся споры о правильном применении серверного и клиентского рендеринга, но вы всегда можете выбрать использование серверного рендеринга для одних страниц и не использовать для других. Некоторые сайты успешно применяют гибридные методы рендеринга. Например, Netflix использует серверный рендеринг для своих относительно статичных целевых страниц, предварительно загружая JavaScript для страниц с высокой интенсивностью взаимодействия, что дает этим более ресурсоемким страницам, отображаемым на стороне клиента, больше шансов на быструю загрузку.
Многие современные фреймворки, библиотеки и архитектуры позволяют отображать одно и то же приложение как на клиенте, так и на сервере. Эти методы можно использовать для серверного рендеринга. Однако архитектуры, в которых рендеринг происходит как на сервере , так и на клиенте, представляют собой отдельный класс решений с совершенно иными характеристиками производительности и компромиссами. Пользователи React могут использовать серверные DOM API или решения, построенные на их основе, такие как Next.js , для серверного рендеринга. Пользователи Vue могут использовать руководство по серверному рендерингу Vue или Nuxt . У Angular есть Universal .
Большинство популярных решений используют ту или иную форму увлажнения, поэтому учитывайте подход, применяемый вашим инструментом.
Статическая отрисовка
Статическая отрисовка происходит во время сборки. Такой подход обеспечивает высокую скорость загрузки (FCP), а также более низкое время отклика (TBT) и время отклика (INP), при условии ограничения количества клиентского JavaScript на страницах. В отличие от серверной отрисовки, он также обеспечивает стабильно высокую скорость отклика (TTFB), поскольку HTML-код страницы не нужно динамически генерировать на сервере. Как правило, статическая отрисовка подразумевает создание отдельного HTML-файла для каждого URL-адреса заранее. Благодаря предварительной генерации HTML-ответов, вы можете развертывать статические рендеры на нескольких CDN, чтобы воспользоваться преимуществами кэширования на периферии сети.

Решения для статической отрисовки бывают самых разных форм и размеров. Такие инструменты, как Gatsby, созданы для того, чтобы разработчики чувствовали, что их приложение отрисовывается динамически, а не генерируется в процессе сборки. Инструменты для генерации статических сайтов, такие как 11ty , Jekyll и Metalsmith, используют свою статическую природу, предлагая более шаблонный подход.
Одним из недостатков статического рендеринга является необходимость генерации отдельных HTML-файлов для каждого возможного URL-адреса. Это может быть сложно или даже невыполнимо, если необходимо заранее предсказать эти URL-адреса, а также для сайтов с большим количеством уникальных страниц.
Пользователи React, возможно, знакомы с Gatsby, статическим экспортом Next.js или Navi , которые позволяют удобно создавать страницы из компонентов. Однако статическая и предварительная отрисовка работают по-разному: статически отрисованные страницы интерактивны и не требуют выполнения большого количества JavaScript на стороне клиента, тогда как предварительная отрисовка улучшает FCP одностраничного приложения, которое должно запускаться на стороне клиента, чтобы сделать страницы по-настоящему интерактивными.
Если вы не уверены, является ли данное решение статическим или предварительным рендерингом, попробуйте отключить JavaScript и загрузить страницу, которую хотите протестировать. Для статически отрисованных страниц большинство интерактивных функций сохраняются и без JavaScript. На страницах с предварительным рендерингом могут присутствовать некоторые базовые функции, такие как ссылки, даже при отключенном JavaScript, но большая часть страницы будет инертной.
Ещё один полезный тест — использовать ограничение скорости сети в инструментах разработчика Chrome и посмотреть, сколько JavaScript-кода загружается, прежде чем страница станет интерактивной. Предварительная отрисовка обычно требует больше JavaScript-кода для перехода в интерактивное состояние, и этот JavaScript, как правило, сложнее, чем подход прогрессивного улучшения, используемый при статической отрисовке.
Рендеринг на стороне сервера против статического рендеринга
Рендеринг на стороне сервера — не лучшее решение для всего, поскольку его динамический характер может привести к значительным вычислительным затратам. Многие решения для рендеринга на стороне сервера не выполняют раннюю очистку буфера, задерживают TTFB или удваивают объем передаваемых данных (например, встроенные состояния, используемые JavaScript на стороне клиента). В React renderToString() может быть медленным, поскольку он синхронный и однопоточный. Более новые API DOM-сервера React поддерживают потоковую обработку, которая позволяет быстрее получить начальную часть HTML-ответа в браузер, в то время как остальная часть еще генерируется на сервере.
Правильная настройка серверного рендеринга может включать в себя поиск или создание решения для кэширования компонентов , управления потреблением памяти, использования методов мемоизации и других аспектов. Часто приходится обрабатывать или перестраивать одно и то же приложение дважды: один раз на стороне клиента и один раз на сервере. Более быстрая отрисовка контента на стороне сервера не обязательно уменьшает объем работы. Даже если после получения HTML-ответа от сервера на стороне клиента выполняется много работы, это все равно может привести к увеличению времени отклика (TBT) и времени отклика (INP) вашего веб-сайта.
Рендеринг на стороне сервера генерирует HTML-код по запросу для каждого URL-адреса, но он может быть медленнее, чем простое отображение статического контента. Если вы готовы приложить дополнительные усилия, рендеринг на стороне сервера в сочетании с кэшированием HTML может значительно сократить время рендеринга на сервере. Преимущество рендеринга на стороне сервера заключается в возможности получать больше «живых» данных и отвечать на более полный набор запросов, чем это возможно при статическом рендеринге. Страницы, требующие персонализации, являются конкретным примером запроса, который плохо работает со статическим рендерингом.
При разработке PWA рендеринг на стороне сервера также может ставить перед пользователями интересные задачи. Лучше ли использовать кэширование сервис-воркеров на уровне всей страницы или рендеринг отдельных фрагментов контента на сервере?
Рендеринг на стороне клиента
Рендеринг на стороне клиента означает отрисовку страниц непосредственно в браузере с помощью JavaScript. Вся логика, получение данных, создание шаблонов и маршрутизация обрабатываются на стороне клиента, а не на сервере. В результате на устройство пользователя передается больше данных с сервера, что влечет за собой ряд компромиссов.
Рендеринг на стороне клиента может быть сложной задачей, требующей высокой скорости для мобильных устройств. Однако, приложив немного усилий для оптимизации JavaScript-кода и обеспечения минимального количества запросов к серверу, можно добиться производительности, почти сравнимой с рендерингом на стороне сервера. Парсер можно ускорить, используя <link rel=preload> для доставки важных скриптов и данных. Мы также рекомендуем рассмотреть использование таких шаблонов, как PRPL , чтобы обеспечить мгновенную навигацию как при первом, так и при последующем переходе.

Основной недостаток рендеринга на стороне клиента заключается в том, что объем необходимого JavaScript, как правило, увеличивается по мере роста приложения, что может повлиять на INP страницы. Это становится особенно проблематичным при добавлении новых библиотек JavaScript, полифилов и стороннего кода, которые конкурируют за вычислительные мощности и часто должны быть обработаны до того, как содержимое страницы сможет отобразиться.
В приложениях, использующих клиентскую отрисовку и больших пакетов JavaScript, следует рассмотреть возможность агрессивного разделения кода для снижения времени отклика (TBT) и времени отклика (INP) во время загрузки страницы, а также отложенную загрузку JavaScript для предоставления пользователю только необходимого контента в нужный момент. Для приложений с низкой или нулевой интерактивностью серверная отрисовка может представлять собой более масштабируемое решение этих проблем.
Для разработчиков одностраничных приложений выявление основных элементов пользовательского интерфейса, общих для большинства страниц, позволяет применять метод кэширования оболочки приложения . В сочетании с сервисными работниками это может значительно улучшить воспринимаемую производительность при повторных посещениях, поскольку страница может очень быстро загружать свой HTML-код оболочки приложения и зависимости из CacheStorage .
Функция регидратации объединяет рендеринг на стороне сервера и на стороне клиента.
Гидратация — это подход, который смягчает компромиссы между рендерингом на стороне клиента и на стороне сервера, выполняя оба процесса. Запросы на навигацию, такие как полная загрузка страницы или перезагрузка, обрабатываются сервером, который рендерит приложение в HTML. Затем JavaScript и данные, используемые для рендеринга, внедряются в результирующий документ. При тщательном подходе это обеспечивает быструю загрузку страницы, подобную серверному рендерингу, а затем «поддерживает» процесс, выполняя повторный рендеринг на стороне клиента.
Это эффективное решение, но оно может иметь существенные недостатки в производительности.
Основной недостаток серверного рендеринга с регидрацией заключается в том, что он может оказать значительное негативное влияние на TBT и INP, даже если улучшает FCP. Страницы, отрисованные на стороне сервера, могут казаться загруженными и интерактивными, но фактически не могут реагировать на ввод, пока не будут выполнены клиентские скрипты для компонентов и не будут прикреплены обработчики событий. На мобильных устройствах это может занять несколько минут, что сбивает с толку и расстраивает пользователя.
Проблема с регидратацией: одно приложение по цене двух.
Для того чтобы JavaScript на стороне клиента корректно продолжил работу с того места, где остановился сервер, без повторного запроса всех данных, которые сервер использовал для рендеринга HTML, большинство решений для рендеринга на стороне сервера сериализуют ответ от зависимостей данных пользовательского интерфейса в виде тегов <script> в документе. Поскольку это приводит к дублированию большого количества HTML-кода, повторная гидратация может вызвать больше проблем, чем просто задержка интерактивности.

В ответ на запрос навигации сервер возвращает описание пользовательского интерфейса приложения, а также исходные данные, использованные для создания этого интерфейса, и полную копию его реализации, которая затем запускается на клиенте. Интерактивность интерфейса начинается только после завершения загрузки и выполнения bundle.js .
Показатели производительности, собранные на реальных веб-сайтах с использованием серверного рендеринга и восстановления данных, указывают на то, что это редко бывает лучшим вариантом. Наиболее важная причина — это влияние на пользовательский опыт, когда страница выглядит готовой, но ни одна из ее интерактивных функций не работает.

Есть надежда на серверный рендеринг с использованием регидрации. В краткосрочной перспективе использование серверного рендеринга только для контента, который легко кэшируется, может сократить время отклика (TTFB), обеспечивая результаты, аналогичные предварительному рендерингу. Постепенная , поэтапная или частичная регидрация может стать ключом к повышению жизнеспособности этого метода в будущем.
Потоковая обработка рендеринга на стороне сервера и постепенное восстановление данных.
За последние несколько лет в области серверного рендеринга произошел ряд изменений.
Потоковая обработка на стороне сервера позволяет отправлять HTML-код порциями, которые браузер может постепенно отображать по мере получения. Это позволяет быстрее доставлять разметку пользователям, ускоряя работу FCP. В React асинхронная обработка потоков в renderToPipeableStream() , в отличие от синхронной renderToString() , обеспечивает эффективную обработку обратного давления.
Также стоит рассмотреть прогрессивную регидрацию ( React её реализовал ). При таком подходе отдельные части приложения, отрисовываемого на сервере, «запускаются» постепенно, в отличие от распространённого сейчас подхода инициализации всего приложения сразу. Это может помочь уменьшить количество JavaScript, необходимого для интерактивности страниц, поскольку позволяет отложить обновление низкоприоритетных частей страницы на стороне клиента, чтобы предотвратить блокировку основного потока, позволяя пользовательскому взаимодействию происходить быстрее после его инициации.
Постепенная регидрация также может помочь избежать одной из самых распространенных ошибок при рендеринге на стороне сервера: дерево DOM, отрисованное на сервере, уничтожается, а затем немедленно перестраивается, чаще всего потому, что для первоначального синхронного рендеринга на стороне клиента требовались данные, которые еще не были готовы, часто это был Promise , который еще не был разрешен.
Частичная регидратация
Частичная регидратация оказалась сложной в реализации. Данный подход является расширением прогрессивной регидратации, которая анализирует отдельные части страницы (компоненты, представления или деревья) и выявляет части с низкой или нулевой интерактивностью. Для каждой из этих преимущественно статических частей соответствующий код JavaScript затем преобразуется в инертные ссылки и декоративные элементы, сводя их нагрузку на стороне клиента практически к нулю.
Подход с частичной гидратацией имеет свои проблемы и компромиссы. Он создает некоторые интересные проблемы для кэширования, а навигация на стороне клиента означает, что мы не можем предполагать, что HTML-код, отрисованный сервером для инертных частей приложения, будет доступен без полной загрузки страницы.
Тризоморфная визуализация
Если вам подходит использование сервис-воркеров , рассмотрите трисоморфный рендеринг. Этот метод позволяет использовать потоковый рендеринг на стороне сервера для начальной навигации или навигации без использования JavaScript, а затем ваш сервис-воркер берет на себя рендеринг HTML для навигации после его установки. Это позволяет поддерживать актуальность кэшированных компонентов и шаблонов, а также обеспечивает навигацию в стиле SPA для рендеринга новых представлений в рамках одной сессии. Этот подход работает лучше всего, когда вы можете использовать один и тот же код шаблонизации и маршрутизации на сервере, клиентской странице и сервис-воркере.

Вопросы SEO
При выборе стратегии веб-рендеринга команды часто учитывают влияние SEO. Серверный рендеринг — популярный выбор для обеспечения «полноценного» внешнего вида, который могут интерпретировать поисковые роботы. Роботы понимают JavaScript , но часто существуют ограничения в способах рендеринга. Клиентский рендеринг также может работать, но часто требует дополнительного тестирования и дополнительных затрат. В последнее время динамический рендеринг также стал вариантом, который стоит рассмотреть, если ваша архитектура в значительной степени зависит от клиентского JavaScript.
Заключение
При выборе подхода к рендерингу измерьте и поймите, в чем заключаются ваши «узкие места». Подумайте, какой подход — статический или серверный — позволит вам добиться наилучших результатов. Вполне допустимо использовать в основном HTML с минимальным количеством JavaScript для обеспечения интерактивности. Вот удобная инфографика, демонстрирующая спектр серверных и клиентских решений:

Кредиты
Спасибо всем за отзывы и вдохновение!
Джеффри Посник, Хуссейн Джирде, Шубхи Паникер, Крис Харрелсон и Себастьян Маркбаге.