Клиентский рендеринг HTML и интерактивность

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

Анализ и рендеринг HTML — это то, что браузеры по умолчанию очень хорошо выполняют для веб-сайтов, использующих встроенную логику навигации браузера, которую иногда называют «традиционной загрузкой страниц» или «жесткой навигацией». Такие веб-сайты иногда называют многостраничными приложениями (MPA).

Однако разработчики могут обойти настройки браузера по умолчанию в соответствии с потребностями своих приложений. Это, безусловно, относится к веб-сайтам, использующим шаблон одностраничного приложения (SPA) , который динамически создает большие части HTML/DOM на клиенте с помощью JavaScript. Рендеринг на стороне клиента — это название этого шаблона проектирования, и он может повлиять на взаимодействие вашего веб-сайта с следующей отрисовкой (INP), если требуемая работа является чрезмерной.

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

Как браузер отображает HTML, предоставленный сервером

Шаблон навигации, используемый при традиционной загрузке страниц, предполагает получение HTML с сервера при каждой навигации. Если вы вводите URL-адрес в адресную строку браузера или нажимаете ссылку в MPA, происходит следующая серия событий:

  1. Браузер отправляет запрос навигации по предоставленному URL-адресу.
  2. Сервер отвечает фрагментами HTML.

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

Скриншот анализа HTML, отправленного сервером, визуализируемый на панели производительности Chrome DevTools. По мере поступления HTML-кода его фрагменты обрабатываются в рамках нескольких более коротких задач, а рендеринг происходит постепенно.
Анализ и рендеринг HTML, предоставленного сервером, как показано на панели производительности Chrome DevTools. Задачи, связанные с анализом HTML и его рендерингом, разделены на блоки.

Как и большинство вещей, происходящих в браузере, анализ HTML происходит внутри задач. Когда HTML передается с сервера в браузер, браузер оптимизирует анализ этого HTML, делая это постепенно, по мере того как биты этого потока поступают порциями. В результате браузер периодически переходит к основному потоку после обработки каждого фрагмента, что позволяет избежать длительных задач . Это означает, что во время анализа HTML может выполняться другая работа, включая работу по поэтапному рендерингу, необходимую для представления страницы пользователю, а также обработку взаимодействий пользователя, которые могут произойти во время критического периода запуска страницы. Такой подход приводит к более высокому показателю взаимодействия с следующей отрисовкой (INP) для страницы.

Вывод? При потоковой передаче HTML с сервера вы получаете бесплатный инкрементный анализ и рендеринг HTML, а также автоматическую передачу в основной поток. Вы не получите этого при рендеринге на стороне клиента.

Как браузер отображает HTML, предоставленный JavaScript

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

Рендеринг на стороне клиента также может происходить в не-SPA в более ограниченных случаях, когда HTML динамически добавляется в DOM через JavaScript.

Существует несколько распространенных способов создания HTML или добавления в DOM с помощью JavaScript:

  1. Свойство innerHTML позволяет вам установить содержимое существующего элемента с помощью строки, которую браузер анализирует в DOM.
  2. Метод document.createElement позволяет создавать новые элементы для добавления в DOM без использования анализа HTML в браузере.
  3. Метод document.write позволяет вам записать HTML в документ (и браузер его анализирует, как и в подходе №1). Однако по ряду причин использование document.write настоятельно не рекомендуется.
Скриншот анализа HTML, визуализированного с помощью JavaScript, визуализируемого на панели производительности Chrome DevTools. Работа выполняется в рамках одной длинной задачи, которая блокирует основной поток.
Анализ и рендеринг HTML с помощью JavaScript на клиенте, как показано на панели производительности Chrome DevTools. Задачи, связанные с его анализом и рендерингом, не разбиваются на части, в результате чего получается длинная задача, блокирующая основной поток.

Последствия создания HTML/DOM с помощью клиентского JavaScript могут быть значительными:

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

Что вы можете сделать с влиянием рендеринга на стороне клиента на производительность

Если ваш веб-сайт сильно зависит от рендеринга на стороне клиента и вы заметили плохие значения INP в данных поля , вам может быть интересно, имеет ли рендеринг на стороне клиента какое-либо отношение к проблеме. Например, если ваш веб-сайт является SPA, ваши полевые данные могут выявить взаимодействия, ответственные за значительную работу по рендерингу.

Какой бы ни была причина, вот несколько потенциальных причин, которые вы можете изучить, чтобы вернуть ситуацию в нужное русло.

Предоставляйте как можно больше HTML с сервера.

Как упоминалось ранее, браузер по умолчанию очень эффективно обрабатывает HTML с сервера. Это позволит разбить синтаксический анализ и рендеринг HTML таким образом, чтобы избежать длительных задач и оптимизировать общее время основного потока. Это приводит к снижению общего времени блокировки (TBT) , а TBT сильно коррелирует с INP .

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

  • В React вам понадобится использовать API-интерфейс Server DOM для рендеринга HTML на сервере. Но имейте в виду: традиционный метод рендеринга на стороне сервера использует синхронный подход , что может привести к увеличению времени до первого байта (TTFB) , а также последующих показателей, таких как First Contentful Paint (FCP) и LCP. По возможности убедитесь, что вы используете API потоковой передачи для Node.js или других сред выполнения JavaScript , чтобы сервер мог начать потоковую передачу HTML в браузер как можно скорее. Next.js — платформа на основе React — по умолчанию предоставляет множество лучших практик. Помимо автоматического отображения HTML на сервере, он также может статически генерировать HTML для страниц, которые не изменяются в зависимости от контекста пользователя (например, аутентификации).
  • Vue также по умолчанию выполняет рендеринг на стороне клиента. Однако, как и React, Vue также может отображать HTML-код вашего компонента на сервере . Либо воспользуйтесь преимуществами этих серверных API, где это возможно, либо рассмотрите абстракцию более высокого уровня для вашего проекта Vue, чтобы упростить реализацию лучших практик.
  • Svelte по умолчанию отображает HTML на сервере , хотя, если коду вашего компонента требуется доступ к эксклюзивным пространствам имен браузера (например, window ), вы не сможете отображать HTML этого компонента на сервере. По возможности изучите альтернативные подходы, чтобы не вызывать ненужного рендеринга на стороне клиента. SvelteKit — который для Svelte является тем же, чем Next.js для React — по возможности внедряет в ваши проекты Svelte множество лучших практик, поэтому вы можете избежать потенциальных ошибок в проектах, которые используют только Svelte.

Ограничьте количество узлов DOM, создаваемых на клиенте.

Когда DOM большие, объем обработки, необходимой для их рендеринга, имеет тенденцию увеличиваться. Независимо от того, является ли ваш веб-сайт полноценным SPA или добавляет новые узлы в существующий DOM в результате взаимодействия с MPA, подумайте о том, чтобы эти DOM были как можно меньшими. Это поможет сократить объем работы, необходимой во время рендеринга на стороне клиента для отображения этого HTML, и, как мы надеемся, поможет снизить INP вашего веб-сайта.

Рассмотрим рабочую архитектуру службы потоковой передачи.

Это продвинутый метод, который может не работать с каждым вариантом использования, но он может превратить ваш MPA в веб-сайт, который загружается мгновенно, когда пользователи переходят с одной страницы на другую. Вы можете использовать сервис-воркера для предварительного кэширования статических частей вашего веб-сайта в CacheStorage , одновременно используя API ReadableStream для получения остальной части HTML-кода страницы с сервера.

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

Короче говоря, рабочая архитектура потокового сервиса не заменяет встроенную логику навигации браузера — она дополняет ее. Дополнительные сведения о том, как добиться этого с помощью Workbox , см. в разделе Ускорение многостраничных приложений с потоками .

Заключение

То, как ваш веб-сайт получает и отображает HTML, влияет на производительность. Когда вы полагаетесь на сервер для отправки всего (или большей части) HTML-кода, необходимого для функционирования вашего веб-сайта, вы получаете многое бесплатно: инкрементный анализ и рендеринг, а также автоматический переход в основной поток, чтобы избежать длительных задач.

Рендеринг HTML на стороне клиента приводит к ряду потенциальных проблем с производительностью, которых во многих случаях можно избежать. Однако из-за требований каждого отдельного веб-сайта этого невозможно избежать в 100% случаев. Чтобы уменьшить потенциально длительные задачи, которые могут возникнуть из-за чрезмерного рендеринга клиентского сайта, убедитесь, что вы отправляете как можно большую часть HTML-кода вашего веб-сайта с сервера, когда это возможно, и держите размеры DOM как можно меньшими для HTML, который должен отображаться на клиент и рассмотрите альтернативные архитектуры для ускорения доставки HTML клиенту, а также использования преимуществ поэтапного анализа и рендеринга, которые браузер обеспечивает для HTML, загружаемого с сервера.

Если вы сможете свести рендеринг на стороне клиента к минимуму, вы улучшите не только INP вашего сайта, но и другие показатели, такие как LCP, TBT и, возможно, даже в некоторых случаях ваш TTFB.

Героическое изображение из Unsplash , автор Maik Jonietz .