Chrome Dev Summit 2020 is back & going virtual on December 9-10. Learn more

Рендеринг в Интернете

Как разработчики, мы часто сталкиваемся с решениями, которые влияют на всю архитектуру наших приложений. Одно из основных решений, которые должны принять веб-разработчики, - это где реализовать логику и рендеринг в своих приложениях. Это может быть сложно, так как существует несколько разных способов создания сайта.

Наше понимание этого основано на нашей работе в Chrome, в которой за последние несколько лет мы общались с крупными сайтами. Вообще говоря, мы бы рекомендовали разработчикам рассмотреть рендеринг сервера или статический рендеринг с использованием подхода полной регидратации.

Чтобы лучше понять архитектуры, которые мы выбираем, когда принимаем это решение, нам необходимо иметь четкое понимание каждого подхода и единую терминологию, которую следует использовать при разговоре о них. Различия между этими подходами помогают проиллюстрировать компромиссы рендеринга в сети через призму производительности.

Терминология

Рендеринг

  • SSR: рендеринг на стороне сервера - рендеринг клиентского или универсального приложения в HTML на сервере.
  • CSR: рендеринг на стороне клиента - рендеринг приложения в браузере, обычно с использованием DOM.
  • Регидратация: «загрузка» представлений JavaScript на клиенте так, чтобы они повторно использовали дерево DOM и данные HTML, представленные сервером.
  • Предварительный рендеринг: запуск приложения на стороне клиента во время сборки для захвата его исходного состояния в виде статического HTML.

Производительность

  • TTFB: время до первого байта - рассматривается как время между нажатием на ссылку и первым поступающим контентом.
  • FP: First Paint - первый раз, когда любой пиксель становится видимым для пользователя.
  • FCP: First Contentful Paint - время, когда запрашиваемый контент (тело статьи и т. Д.) Становится видимым.
  • TTI: Time To Interactive - время, когда страница становится интерактивной (события связаны и т. Д.).

Серверный рендеринг

Серверный рендеринг генерирует полный HTML для страницы на сервере в ответ на навигацию. Это позволяет избежать дополнительных циклов обработки данных и шаблонов на клиенте, поскольку они обрабатываются до того, как браузер получает ответ.

Серверный рендеринг обычно производит быструю First Paint и First Contentful Paint. Выполнение логики страницы и рендеринга на сервере позволяет избежать отправки большого количества JavaScript клиенту, что помогает быстро достичь Time to Interactive. Это имеет смысл, поскольку при серверном рендеринге вы просто отправляете текст и ссылки в браузер пользователя. Этот подход может хорошо работать для широкого спектра устройств и условий сети, и открывает интересные оптимизации браузера, такие как потоковый анализ документов.

Diagram showing server rendering and JS execution affecting FCP and TTI

При использовании серверного рендеринга пользователи вряд ли будут ждать, пока обработается привязанный к процессору JavaScript, прежде чем они смогут использовать ваш сайт. Даже когда нельзя избежать стороннего JS, использование серверного рендеринга для сокращения собственных затрат на JS может дать вам больше « бюджета » на все остальное. Однако у этого подхода есть один главный недостаток: генерация страниц на сервере требует времени, что часто может привести к более долгому времени до первого байта.

Достаточно ли серверного рендеринга для вашего приложения, во многом зависит от него самого. Существует давняя дискуссия о правильном применении серверного рендеринга по сравнению с рендерингом на стороне клиента, но важно помнить, что вы можете использовать серверный рендеринг для одних страниц, а не для других нет. Некоторые сайты успешно применяют гибридные методы рендеринга. Сервер Netflix отображает свои относительно статичные целевые страницы, предварительно выбирая JS для страниц с интенсивным взаимодействием, предоставляя этим более тяжелым страницам, отображаемым клиентом, более высокую вероятность быстрой загрузки.

Многие современные фреймворки, библиотеки и архитектуры позволяют отображать одно и то же приложение как на клиенте, так и на сервере. Эти методы могут использоваться для серверного рендеринга, однако важно отметить, что архитектуры, в которых рендеринг происходит как на сервере, так и на клиенте, представляют собой собственный класс решений с очень разными характеристиками производительности и компромиссами. Пользователи React могут использовать renderToString() или решения, построенные на его основе, такие как Next.js, для серверного рендеринга. Пользователи Vue могут взглянуть на руководство по серверному рендерингу Vue или Nuxt. Angular имеет Universal. В большинстве популярных решений используется некоторая форма гидратации, поэтому перед выбором инструмента ознакомьтесь с подходом, который используется.

Статический рендеринг

Статический рендеринг происходит во время сборки и предлагает быстрые First Paint, First Contentful Paint и Time To Interactive - при условии, что количество JS на стороне клиента ограничено. В отличие от серверного рендеринга, ему также удается достичь стабильно быстрого времени до первого байта, поскольку HTML-код для страницы не нужно генерировать на лету. Как правило, статический рендеринг означает создание отдельного HTML-файла для каждого URL-адреса заранее. Поскольку HTML-ответы генерируются заранее, статические рендеры могут быть развернуты на нескольких CDN, чтобы воспользоваться преимуществом пограничного кэширования (edge-caching).

Diagram showing static rendering and optional JS execution affecting FCP
and TTI

Решения для статического рендеринга бывают всех форм и размеров. Такие инструменты как Gatsby спроектированы так, чтобы разработчики почувствовали что их приложения рендерятся динамически быстрее чем генерируются на этапе сборки. Другие, как Jekyll и Metalsmith принимают их статическую природу, обеспечивая более шаблонный подход.

Одним из недостатков статического рендеринга является то, что отдельные HTML-файлы должны создаваться для каждого возможного URL. Это может быть сложно или даже невозможно, если вы не можете предсказать, какие будут эти URL-адреса раньше времени, или для сайтов с большим количеством уникальных страниц.

Пользователи React могут быть знакомы с Gatsby , статическим экспортом Next.js или Navi - все это делает его удобным для автора с использованием компонентов. Тем не менее, важно понимать разницу между статическим рендерингом и предварительным рендерингом: статические рендеринг страниц являются интерактивными без необходимости выполнения большого количества JS на стороне клиента, тогда как предварительный рендеринг улучшает First Paint или First Contentful Paint одностраничного приложения (SPA), которое необходимо загрузить клиент для того, чтобы страницы были по-настоящему интерактивными.

Если вы не уверены, является ли данное решение статическим или предварительным рендерингом, попробуйте этот тест: отключите JavaScript и загрузите созданные веб-страницы. Для статически визуализированных страниц большая часть функциональности будет по-прежнему существовать без включенного JavaScript. Для предварительно обработанных страниц могут существовать некоторые базовые функции, такие как ссылки, но большая часть страницы будет инертной.

Еще один полезный тест - замедление работы сети с помощью Chrome DevTools и наблюдение за загрузкой JavaScript до того, как страница станет интерактивной. Для предварительного рендеринга обычно требуется больше JavaScript, чтобы стать интерактивным, и этот JavaScript имеет тенденцию быть более сложным, чем подход прогрессивного улучшения, используемый при статическом рендеринге.

Серверный Рендеринг против Статического Рендеринга

Серверный Рендеринг не является серебряной пулей - его динамическая природа может сопровождаться значительными вычислительными накладными расходами. Многие решения для рендеринга серверов не сбрасываются рано, могут задержать TTFB или удвоить отправку данных (например, встроенное состояние, используемое JS на клиенте). В React, функция renderToString() может быть медленной, поскольку она является синхронной и однопоточной. Получение серверным рендерингом «права» может включать в себя нахождение или создание решения для компонентов кэширования , управление потреблением памяти, применение техник мемоизации, и многие другие проблемы. Обычно вы обрабатываете / перестраиваете одно и то же приложение несколько раз - один раз на клиенте и один раз на сервере. Тот факт, что при рендеринге с сервера может появиться что-то раньше, не означает, что у вас меньше работы.

Серверный рендеринг генерирует HTML по требованию для каждого URL, но может быть медленнее, чем просто обслуживание статического рендеринга контента. Если вы можете добавить дополнительную работу, серверный рендеринг + кэширование HTML может значительно сократить время серверного рендеринга. Преимуществом рендеринга на сервере является возможность извлекать больше «живых» данных и отвечать на более полный набор запросов, чем это возможно при статическом рендеринге. Страницы, требующие персонализации, являются конкретным примером типа запроса, который не будет хорошо работать при статическом рендеринге.

Серверный рендеринг также может представлять интересные решения при построении PWA. Лучше использовать полностраничное сервис-воркер (service-worker) кэширование или просто рендерить отдельные части контента на сервере?

Рендеринг на стороне клиента (CSR)

Рендеринг на стороне клиента (CSR) означает рендеринг страниц непосредственно в браузере с использованием JavaScript. Вся логика, выборка данных, шаблоны и маршрутизация обрабатываются на клиенте, а не на сервере.

Рендеринг на стороне клиента может быть трудно получить и быстро сохранить для мобильных устройств. Он может приблизиться к производительности чисто серверного рендеринга, если выполняет минимальную работу, сохраняя жесткий бюджет JavaScript и предоставляя ценность в минимально возможном количестве RTTs. Критические сценарии и данные могут быть доставлены быстрее, используя HTTP/2 Server Push или <link rel=preload>, что заставляет парсер работать на вас быстрее. Шаблоны, такие как PRPL, стоит оценить, чтобы обеспечить мгновенную начальную и последующую навигацию.

Diagram showing client-side rendering affecting FCP and TTI

Основным недостатком рендеринга на стороне клиента является то, что количество требуемого JavaScript имеет тенденцию к росту по мере роста приложения. Это становится особенно трудным с добавлением новых библиотек JavaScript, полифилов и стороннего кода, которые конкурируют за вычислительную мощность и часто должны обрабатываться до того, как содержимое страницы может быть отображено. Опыт работы с CSR, основанный на больших пакетах JavaScript, должен учитывать агрессивное разделение кода и обязательно загружать JavaScript - «обслуживайте только то, что вам нужно, когда вам это нужно». Для случаев, когда интерактивность незначительна или отсутствует, рендеринг сервера может представлять собой более масштабируемое решение этих проблем.

Для людей, создающих одностраничное приложение, идентификация основных частей пользовательского интерфейса, используемых большинством страниц, означает, что вы можете применить технику кэширования Application Shell . В сочетании с сервис-воркерами это может значительно улучшить воспринимаемую производительность при повторных посещениях.

Объединение серверного рендеринга и CSR через регидратацию

Этот подход, часто называемый универсальным рендерингом или просто «SSR». Этот подход пытается сгладить углы между рендерингом на стороне клиента и рендерингом сервера, выполняя оба действия. Запросы навигации, такие как полная загрузка или перезагрузка страницы, обрабатываются сервером, который отображает приложение в HTML, затем JavaScript и данные, используемые для визуализации, встраиваются в итоговый документ. При аккуратной реализации это обеспечивает быструю First Contentful Paint точно так же, как серверный рендеринг, а затем «подхватывает», снова выполняя рендеринг на клиенте, используя технику, называемую (ре)гидратация. Это новое решение, но оно может иметь некоторые существенные недостатки производительности.

Основным недостатком SSR с регидратацией является то, что он может оказать существенное негативное влияние на Time To Interactive, даже если он улучшит First Paint. Страницы SSR часто выглядят обманчиво загруженными и интерактивными, но на самом деле не могут реагировать на ввод, пока JS на стороне клиента не будет выполнен и обработчики событий не присоединены. Это может занять несколько секунд или даже минут на мобильном телефоне.

Возможно, вы испытали это сами - в течение некоторого времени после того, как страница, которая выглядит полностью загруженной не реагирует на клики или нажатия. Это быстро расстраивает ... «Почему ничего не происходит? Почему я не могу прокрутить?

Проблема регидратации: одно приложение по цене двух

Проблемы регидратации часто могут быть хуже, чем замедленная интерактивность из-за JS. Чтобы клиентский JavaScript мог точно «подхватить», где сервер остановился, без необходимости повторного запроса всех данных, которые сервер использовал для визуализации своего HTML, текущие SSR решения обычно сериализуют ответ от пользовательского интерфейса. зависимости данных в документе в виде тегов скрипта. Полученный HTML-документ содержит высокий уровень дублирования:

HTML document
containing serialized UI, inlined data and a bundle.js script

Как вы можете видеть, сервер возвращает описание пользовательского интерфейса приложения в ответ на запрос навигации, но он также возвращает исходные данные, использованные для создания этого пользовательского интерфейса, и полную копию реализации пользовательского интерфейса, которая затем загружается на клиенте. Только после завершения загрузки и выполнения bundle.js этот интерфейс становится интерактивным.

Метрики производительности, собранные с реальных веб-сайтов с использованием регидратации SSR, указывают на то, что его использование не рекомендуется. В конечном счете, причина кроется в пользовательском опыте: в конечном итоге крайне просто оставить пользователей в «странной долине».

Diagram showing client rendering negatively affecting TTI

Хотя есть надежда на SSR с регидратацией. В краткосрочной перспективе только использование SSR для контента с высокой степенью кэширования может уменьшить задержку TTFB, что дает результаты, аналогичные предварительному рендерингу. Регидратация постепенно, постепенно или частично может стать ключом к повышению эффективности этого метода в будущем.

Рендеринг потокового сервера и прогрессивная регидратация

За последние несколько лет серверный рендеринг получил ряд разработок.

Рендеринг потокового сервера позволяет отправлять HTML порциями, которые браузер может визуализировать по мере получения. Это может обеспечить быструю First Paint и First Contentful Paint, поскольку разметка поступает к пользователям быстрее. В React потоки, являющиеся асинхронными в renderToNodeStream() - по сравнению с синхронным renderToString - означают, что обратное давление хорошо обрабатывается.

Прогрессивная регидратация также стоит того, чтобы за ней следить, и кое-что, что изучал React. При таком подходе отдельные части приложения, отображаемого на сервере, «загружаются» с течением времени, а не по общему текущему подходу - инициализации всего приложения сразу. Это может помочь уменьшить объем JavaScript, необходимый для того, чтобы сделать страницы интерактивными, поскольку обновление на стороне клиента низкоприоритетных частей страницы может быть отложено для предотвращения блокировки основного потока. Это также может помочь избежать одной из самых распространенных ошибок регидратации в SSR, когда дерево DOM, отображаемое сервером, разрушается, а затем немедленно перестраивается - чаще всего потому, что при первоначальной синхронной визуализации на стороне клиента требуются не совсем готовые данные, возможно, ожидающие разрешения Promise.

Частичная регидратация

Частичная регидратация оказалась трудно осуществимой. Этот подход является продолжением идеи прогрессивной регидратации, где анализируются отдельные части (компоненты/виды/деревья), которые должны быть постепенно регидратированы, и идентифицируются те, у которых мало интерактивности или нет реактивности. Для каждой из этих в основном статических частей соответствующий код JavaScript затем преобразуется в инертные ссылки и декоративную функциональность, сокращая их площадь на стороне клиента почти до нуля. Подход частичной гидратации имеет свои проблемы и недостатки. Это создает некоторые интересные проблемы для кэширования, и навигация на стороне клиента означает, что мы не можем предполагать, что серверный HTML-код для инертных частей приложения будет доступен без полной загрузки страницы.

Трисоморфный рендеринг

Если сервис-воркеры являются подходящим вариантом для вас, «трисоморфный» рендеринг также может представлять интерес. Это метод, при котором вы можете использовать потоковый рендеринг сервера для начальной/не-JS-навигации, а затем попросить сервис-воркер на себя рендеринг HTML для навигации после его установки. Это может поддерживать кэшированные компоненты и шаблоны в актуальном состоянии и обеспечивает навигацию в стиле SPA для отображения новых представлений в одном сеансе. Этот подход работает лучше всего, когда вы можете совместно использовать один и тот же код шаблонов и маршрутизации между сервером, клиентской страницей и сервис-воркером.

Diagram of Trisomorphic rendering, showing a browser and service worker
communicating with the server

SEO соображения

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

В случае сомнений инструмент Mobile Friendly Test неоценим для проверки того, что выбранный вами подход делает то, на что вы надеетесь. Он показывает визуальный предварительный просмотр того, как какая-либо страница отображается сканеру Google, найденный сериализованный контент HTML (после выполнения JavaScript) и любые ошибки, обнаруженные во время рендеринга.

Screenshot of the Mobile Friendly Test UI

Завершение ...

При выборе подхода к визуализации измерьте и поймите, какие у вас узкие места. Подумайте, может ли статический рендеринг или рендеринг сервера получить вам 90% пути. Совершенно нормально в основном отправлять HTML с минимальным JS, чтобы получить интерактивный опыт. Вот удобная инфографика, показывающая спектр сервер-клиент:

Infographic showing the spectrum of options described in this article

Благодарности

Спасибо всем за их отзывы и вдохновение:

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

Was this page helpful?
Yes
What was the best thing about this page?
It helped me complete my goal(s)
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
It had the information I needed
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
It had accurate information
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
It was easy to read
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
Something else
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
No
What was the worst thing about this page?
It didn't help me complete my goal(s)
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
It was missing information I needed
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
It had inaccurate information
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
It was hard to read
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.
Something else
Thank you for the feedback. If you have specific ideas on how to improve this page, please create an issue.

Subscribe to our RSS or Atom feed and get the latest updates in your favorite feed reader!