Дата публикации: 6 февраля 2019 г. Последнее обновление: 5 января 2026 г.
Одно из основных решений, которое должны принять веб-разработчики, – где реализовать логику и рендеринг в своем приложении. Это может быть сложно, потому что существует множество способов создания веб-сайта.
Наше понимание этой области основано на работе с Chrome и общении с представителями крупных сайтов в течение последних нескольких лет. В целом мы рекомендуем разработчикам использовать отрисовку на стороне сервера или статическую отрисовку, а не полную регидратацию.
Чтобы лучше понять, из каких архитектур мы выбираем, нам нужна единая терминология и общая структура для каждого подхода. Это поможет вам выбрать оптимальный подход к отрисовке с точки зрения эффективности страницы.
Терминология
Сначала мы определим некоторые термины, которые будем использовать.
Визуализация
- Серверная отрисовка (SSR)
- Отрисовка приложения на сервере для отправки HTML, а не JavaScript, клиенту.
- Клиентская отрисовка
- Отрисовка приложения в браузере с использованием JavaScript для изменения DOM.
- Предварительная отрисовка
- Запуск клиентского приложения во время сборки, чтобы зафиксировать его исходное состояние в виде статического HTML-кода. Обратите внимание, что предварительная отрисовка в этом смысле отличается от предварительной отрисовки будущих переходов в браузере.
- Гидратация
- Выполнение клиентских скриптов для добавления состояния приложения и интерактивности в HTML, обработанный на стороне сервера. Гидратация предполагает, что DOM не меняется.
- Регидратация
- Хотя этот термин часто используется в том же значении, что и гидратация, регидратация подразумевает регулярное обновление DOM с учетом последнего состояния, в том числе после первоначальной гидратации.
Эффективность
- Время до первого байта (TTFB)
- Время между нажатием на ссылку и загрузкой первого байта контента на новой странице.
- Первая отрисовка контента (FCP)
- Время, когда запрошенный контент (текст статьи и т. д.) становится видимым.
- Interaction to Next Paint (INP)
- Показатель, который позволяет оценить, насколько быстро страница реагирует на действия пользователя в целом.
- Общее время блокировки (TBT)
- Прокси-показатель для INP, который рассчитывает, как долго основной поток был заблокирован во время загрузки страницы.
Отрисовка на стороне сервера
При отрисовке на стороне сервера в ответ на навигацию генерируется полный HTML-код страницы. Это позволяет избежать дополнительных циклов обмена данными туда и обратно для получения данных и шаблонов на клиенте, поскольку средство визуализации обрабатывает их до того, как браузер получит ответ.
Отрисовка на стороне сервера обычно обеспечивает быструю первую отрисовку контента. Выполнение логики страницы и рендеринг на сервере позволяют избежать отправки большого количества JavaScript клиенту. Это помогает снизить TBT страницы, что также может привести к снижению INP, поскольку основной поток не блокируется так часто во время загрузки страницы. Если основной поток блокируется реже, у взаимодействий пользователя больше возможностей выполняться быстрее.
Это логично, поскольку при отрисовке на стороне сервера вы просто отправляете текст и ссылки в браузер пользователя. Этот подход может хорошо работать для различных условий устройства и сети и открывает интересные оптимизации браузера, такие как потоковый синтаксический анализ документов.
При отрисовке на стороне сервера пользователям не приходится ждать, пока выполнится код JavaScript, требующий ресурсов процессора. Даже если вы не можете избежать использования стороннего кода JavaScript, применение отрисовки на стороне сервера для снижения затрат на собственный код JavaScript позволит вам выделить больше бюджета на все остальное. Однако у этого подхода есть один потенциальный недостаток: создание страниц на сервере занимает время, что может увеличить TTFB страницы.
Достаточно ли для вашего приложения отрисовки на сервере, зависит от того, что именно вы создаете. Споры о том, какой из этих подходов лучше, ведутся уже давно, но вы можете использовать серверный рендеринг для одних страниц и клиентский рендеринг для других. Некоторые сайты успешно используют гибридные методы отрисовки. Например, Netflix использует серверный рендеринг для своих относительно статичных целевых страниц, а также prefetching JavaScript для страниц с большим количеством интерактивных элементов. Это позволяет ускорить загрузку страниц, для которых используется клиентский рендеринг.
Многие современные фреймворки, библиотеки и архитектуры позволяют выполнять рендеринг одного и того же приложения как на стороне клиента, так и на стороне сервера. Эти методы можно использовать для отрисовки на стороне сервера. Однако архитектуры, в которых рендеринг происходит как на сервере, так и на клиенте, представляют собой собственный класс решений с совершенно разными характеристиками производительности и компромиссами. Пользователи React могут использовать серверные API DOM или решения на их основе, например Next.js, для отрисовки на стороне сервера. Пользователи Vue могут ознакомиться с руководством по обработке на стороне сервера или использовать Nuxt. В Angular есть Universal.
Однако в большинстве популярных решений используется та или иная форма гидратации, поэтому вам нужно знать, какие подходы применяются в вашем инструменте.
Статическая отрисовка
Статическая отрисовка происходит во время сборки. Этот подход обеспечивает быстрый FCP, а также более низкие TBT и INP, если вы ограничиваете объем клиентского JavaScript на своих страницах. В отличие от отрисовки на стороне сервера, при этом также достигается стабильно быстрое время до первого байта, поскольку HTML-код страницы не нужно динамически генерировать на сервере. Как правило, статическая отрисовка означает, что для каждого URL заранее создается отдельный HTML-файл. Если HTML-ответы создаются заранее, вы можете развернуть статические отрисовки в нескольких сетях доставки контента, чтобы использовать пограничное кеширование.
Решения для статического рендеринга бывают разных видов и размеров. Такие инструменты, как Gatsby, созданы для того, чтобы разработчики чувствовали, что их приложение отображается динамически, а не генерируется на этапе сборки. Инструменты для создания статических сайтов, такие как 11ty, Jekyll и Metalsmith, используют статический подход, предоставляя больше возможностей для работы с шаблонами.
Один из недостатков статической отрисовки заключается в том, что она должна генерировать отдельные HTML-файлы для каждого возможного URL. Это может быть сложно или даже невозможно, если вам нужно заранее предсказать эти URL и если на сайте много уникальных страниц.
Пользователи React могут быть знакомы с Gatsby, Next.js static export или Navi. Все эти инструменты позволяют создавать страницы из компонентов. Однако между статической и предварительной отрисовкой есть разница. Статически отрисованные страницы интерактивны без необходимости выполнять большой объем клиентского кода JavaScript. Предварительная отрисовка улучшает показатель FCP одностраничного приложения, которое должно быть запущено на стороне клиента, чтобы страницы стали по-настоящему интерактивными.
Если вы не уверены, является ли данное решение статической визуализацией или предварительной отрисовкой, попробуйте отключить JavaScript и загрузите страницу, которую хотите проверить. На страницах со статическим рендерингом большинство интерактивных функций работает и без JavaScript. На предварительно отрисованных страницах могут работать некоторые базовые функции, например ссылки с отключенным JavaScript, но большая часть страницы неактивна.
Ещё один полезный тест – использовать ограничение пропускной способности сети в инструментах разработчика Chrome и посмотреть, сколько JavaScript скачивается, прежде чем страница станет интерактивной. Для обеспечения интерактивности при предварительной отрисовке обычно требуется больше кода JavaScript, и он, как правило, сложнее, чем при прогрессивном улучшении, которое используется при статической отрисовке.
Обработка на стороне сервера и статичная отрисовка
Отрисовка на стороне сервера не является лучшим решением для всего, поскольку ее динамический характер может повлечь за собой значительные накладные расходы на вычисления. Многие решения для рендеринга на стороне сервера не выполняют раннюю очистку, задерживают TTFB или удваивают объем отправляемых данных (например, встроенные состояния, используемые JavaScript на клиенте). В React renderToString() может работать медленно, поскольку является синхронным и однопоточным.
Более новые API React Server DOM поддерживают потоковую передачу, которая позволяет быстрее передавать в браузер начальную часть 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 и данные, используемые для отрисовки, встраиваются в итоговый документ. Если все сделать правильно, то можно добиться быстрой отрисовки FCP, как при обработке на стороне сервера, а затем продолжить отрисовку на стороне клиента.
Это эффективное решение, но оно может значительно снизить производительность.
Основной недостаток серверной отрисовки с регидратацией заключается в том, что она может значительно ухудшить показатели 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. Отрисовка на сервере – это популярный способ создания страниц, которые выглядят готовыми и могут быть обработаны поисковыми роботами. Поисковые роботы могут интерпретировать JavaScript, но при этом часто возникают ограничения, связанные с отрисовкой. Клиентская отрисовка возможна, но часто требует дополнительного тестирования и ресурсов. Если архитектура вашего сайта в значительной степени зависит от клиентского JavaScript, то вам также может подойти динамическая отрисовка.
Заключение
При выборе подхода к рендерингу измерьте и поймите, в чем заключаются ваши узкие места. Подумайте, можно ли в вашем случае использовать статичную отрисовку или отрисовку на стороне сервера. Вполне нормально отправлять в основном HTML с минимальным количеством JavaScript, чтобы сделать страницу интерактивной. Вот удобная инфографика, показывающая спектр сервер-клиент:
Использованное ПО с открытым исходным кодом
Благодарим всех за отзывы и вдохновение:
Джеффри Посник (Jeffrey Posnick), Хуссейн Джирде (Houssein Djirdeh), Шубхи Паникер (Shubhie Panicker), Крис Харрельсон (Chris Harrelson) и Себастьян Маркбоге (Sebastian Markbåge).