Front-End Performance 2021: оптимизация доставки
Опубликовано: 2022-03-10Это руководство было любезно поддержано нашими друзьями из LogRocket, службы, которая сочетает в себе мониторинг производительности внешнего интерфейса , воспроизведение сеанса и аналитику продукта, чтобы помочь вам повысить качество обслуживания клиентов. LogRocket отслеживает ключевые показатели, в т.ч. DOM завершен, время до первого байта, задержка первого ввода, использование клиентского ЦП и памяти. Получите бесплатную пробную версию LogRocket сегодня.
Оглавление
- Подготовка: планирование и показатели
- Постановка реалистичных целей
- Определение среды
- Оптимизация активов
- Оптимизация сборки
- Оптимизация доставки
- Сеть, HTTP/2, HTTP/3
- Тестирование и мониторинг
- Быстрые победы
- Все на одной странице
- Скачать контрольный список (PDF, Apple Pages, MS Word)
- Подпишитесь на нашу рассылку по электронной почте, чтобы не пропустить следующие руководства.
Оптимизация доставки
- Используем ли мы
defer
для асинхронной загрузки критического JavaScript?
Когда пользователь запрашивает страницу, браузер извлекает HTML и создает DOM, затем извлекает CSS и создает CSSOM, а затем создает дерево рендеринга, сопоставляя DOM и CSSOM. Если необходимо разрешить какой-либо JavaScript, браузер не начнет отображать страницу до тех пор, пока он не будет разрешен, что приведет к задержке отображения. Как разработчики, мы должны явно указать браузеру не ждать и начать рендеринг страницы. Для скриптов это можно сделать с помощью атрибутовdefer
иasync
в HTML.На практике оказывается, что лучше использовать
defer
вместоasync
. Ах, какая опять разница ? По словам Стива Содерса, как только приходятasync
скрипты, они сразу же выполняются — как только скрипт готов. Если это происходит очень быстро, например, когда скрипт уже находится в кеше, он может фактически заблокировать анализатор HTML. Сdefer
браузер не выполняет скрипты до тех пор, пока не будет проанализирован HTML. Итак, если вам не нужно, чтобы JavaScript выполнялся перед началом рендеринга, лучше использоватьdefer
. Кроме того, несколько асинхронных файлов будут выполняться в недетерминированном порядке.Стоит отметить, что существует несколько неправильных представлений об
async
иdefer
. Самое главное,async
не означает, что код будет выполняться всякий раз, когда сценарий готов; это означает, что он будет запускаться всякий раз, когда сценарии будут готовы и будет выполнена вся предыдущая работа по синхронизации. По словам Гарри Робертса, «если вы поместитеasync
сценарий после сценариев синхронизации, вашasync
сценарий будет настолько же быстрым, как и ваш самый медленный сценарий синхронизации».Кроме того, не рекомендуется использовать одновременно
async
иdefer
. Современные браузеры поддерживают оба атрибута, но всякий раз, когда используются оба атрибута,async
всегда будет побеждать.Если вы хотите углубиться в детали, Милица Михайлия написала очень подробное руководство по ускоренному построению DOM, в котором подробно рассматриваются спекулятивный анализ, асинхронность и отложенное выполнение.
- Ленивая загрузка дорогих компонентов с IntersectionObserver и подсказками приоритета.
В общем, рекомендуется лениво загружать все дорогостоящие компоненты, такие как тяжелый JavaScript, видео, фреймы, виджеты и, возможно, изображения. Нативная отложенная загрузка уже доступна для изображений и фреймов с атрибутомloading
(только Chromium). Под капотом этот атрибут откладывает загрузку ресурса до тех пор, пока он не достигнет расчетного расстояния от области просмотра.<!-- Lazy loading for images, iframes, scripts. Probably for images outside of the viewport. --> <img loading="lazy" ... /> <iframe loading="lazy" ... /> <!-- Prompt an early download of an asset. For critical images, eg hero images. --> <img loading="eager" ... /> <iframe loading="eager" ... />
Этот порог зависит от нескольких вещей, от типа загружаемого ресурса изображения до эффективного типа соединения. Но эксперименты, проведенные с использованием Chrome на Android, показывают, что в 4G 97,5% изображений ниже сгиба, загружаемых отложенно, были полностью загружены в течение 10 мс после того, как стали видимыми, поэтому это должно быть безопасно.
Мы также можем использовать атрибут
importance
(high
илиlow
) для элемента<script>
,<img>
или<link>
(только для Blink). На самом деле, это отличный способ снизить приоритет изображений в каруселях, а также изменить приоритет сценариев. Однако иногда нам может понадобиться немного более детальный контроль.<!-- When the browser assigns "High" priority to an image, but we don't actually want that. --> <img src="less-important-image.svg" importance="low" ... /> <!-- We want to initiate an early fetch for a resource, but also deprioritize it. --> <link rel="preload" importance="low" href="/script.js" as="script" />
Самый эффективный способ сделать несколько более сложную ленивую загрузку — использовать Intersection Observer API, который позволяет асинхронно отслеживать изменения в пересечении целевого элемента с элементом-предком или с окном просмотра документа верхнего уровня. По сути, вам нужно создать новый объект
IntersectionObserver
, который получает функцию обратного вызова и набор параметров. Затем мы добавляем цель для наблюдения.Функция обратного вызова выполняется, когда цель становится видимой или невидимой, поэтому, когда она перехватывает область просмотра, вы можете начать выполнять некоторые действия до того, как элемент станет видимым. На самом деле, у нас есть детальный контроль над тем, когда должен вызываться обратный вызов наблюдателя, с
rootMargin
(поле вокруг корня) иthreshold
(одно число или массив чисел, которые указывают, на какой процент видимости цели мы нацелены).Алехандро Гарсия Англада опубликовал удобный учебник о том, как это реализовать на самом деле, Рахул Нанвани написал подробный пост о ленивой загрузке изображений переднего плана и фона, а Google Fundamentals также предоставил подробный учебник по ленивой загрузке изображений и видео с помощью Intersection Observer.
Помните художественные лонгриды с движущимися и липкими объектами? С помощью Intersection Observer также можно реализовать эффективное прокручивание.
Проверьте еще раз, что еще вы могли бы лениво загрузить. Даже ленивая загрузка строк перевода и эмодзи может помочь. Таким образом, Mobile Twitter удалось добиться ускорения выполнения JavaScript на 80 % благодаря новому конвейеру интернационализации.
Небольшое предостережение: стоит отметить, что отложенная загрузка должна быть скорее исключением, чем правилом. Вероятно, нецелесообразно лениво загружать все, что вы действительно хотите, чтобы люди увидели быстро, например, изображения страниц продукта, основные изображения или скрипт, необходимый для того, чтобы основная навигация стала интерактивной.
- Загружайте изображения постепенно.
Вы даже можете поднять ленивую загрузку на новый уровень, добавив прогрессивную загрузку изображений на свои страницы. Как и в Facebook, Pinterest, Medium и Wolt, вы можете сначала загрузить низкокачественные или даже размытые изображения, а затем, когда страница продолжает загружаться, заменить их полноценными версиями с помощью технологии BlurHash или LQIP (заполнители изображений низкого качества). техника.Мнения расходятся, улучшают ли эти методы взаимодействие с пользователем или нет, но они определенно сокращают время до первой отрисовки контента. Мы даже можем автоматизировать его, используя SQIP, который создает низкокачественную версию изображения в качестве заполнителя SVG, или заполнители Gradient Image Placeholders с линейными градиентами CSS.
Эти заполнители могут быть встроены в HTML, поскольку они естественным образом хорошо сжимаются методами сжатия текста. В своей статье Дин Хьюм описал, как эту технику можно реализовать с помощью Intersection Observer.
Отступать? Если браузер не поддерживает наблюдатель пересечений, мы все равно можем отложенно загрузить полифилл или сразу загрузить изображения. И для этого есть даже библиотека.
Хотите пофантазировать? Вы можете отслеживать свои изображения и использовать примитивные формы и края для создания легкого заполнителя SVG, сначала загрузить его, а затем перейти от векторного изображения заполнителя к (загруженному) растровому изображению.
- Вы откладываете рендеринг с помощью
content-visibility
?
Для сложного макета с большим количеством блоков контента, изображений и видео декодирование данных и рендеринг пикселей может быть довольно дорогостоящей операцией, особенно на недорогих устройствах. С помощьюcontent-visibility: auto
мы можем предложить браузеру пропустить макет дочерних элементов, пока контейнер находится за пределами области просмотра.Например, вы можете пропустить рендеринг нижнего колонтитула и поздних разделов при начальной загрузке:
footer { content-visibility: auto; contain-intrinsic-size: 1000px; /* 1000px is an estimated height for sections that are not rendered yet. */ }
Обратите внимание, что content-visibility: auto; ведет себя как переполнение: скрыто; , но вы можете исправить это, применив
padding-left
иpadding-right
вместо значения по умолчаниюmargin-left: auto;
,margin-right: auto;
и заявленная ширина. Заполнение в основном позволяет элементам переполнять блок содержимого и входить в блок заполнения, не выходя из модели блока в целом и не обрезаясь.Кроме того, имейте в виду, что вы можете ввести некоторые CLS, когда новый контент в конечном итоге будет отображаться, поэтому рекомендуется использовать
contain-intrinsic-size
с заполнителем правильного размера ( спасибо, Уна! ).У Thijs Terluin есть гораздо больше подробностей об обоих свойствах и о том, как браузер вычисляет вложенный
contain-intrinsic-size
содержимого, Malte Ubl показывает, как вы можете его вычислить, а краткое пояснение Джейка и Сурма объясняет, как все это работает.И если вам нужно получить немного больше детализации, с помощью CSS Containment вы можете вручную пропустить макет, стиль и работу по рисованию для потомков узла DOM, если вам нужен только размер, выравнивание или вычисленные стили для других элементов — или элемент в настоящее время вне холста.
- Вы откладываете декодирование с помощью
decoding="async"
?
Иногда контент появляется за кадром, но мы хотим быть уверены, что он доступен, когда он нужен клиентам — в идеале, ничего не блокируя на критическом пути, а декодируя и отображая асинхронно. Мы можем использоватьdecoding="async"
, чтобы дать браузеру разрешение на декодирование изображения вне основного потока, избегая воздействия на пользователя процессорного времени, используемого для декодирования изображения (через Malte Ubl):<img decoding="async" … />
В качестве альтернативы, для закадровых изображений мы можем сначала отобразить заполнитель, а когда изображение находится в области просмотра, используя IntersectionObserver, инициировать сетевой вызов для загрузки изображения в фоновом режиме. Кроме того, мы можем отложить рендеринг до декодирования с помощью img.decode() или загрузить изображение, если API декодирования изображений недоступен.
Например, при рендеринге изображения мы можем использовать плавную анимацию. Кэти Хемпениус и Эдди Османи поделились своими мыслями в своем выступлении «Скорость в масштабе: советы и хитрости веб-производительности из окопов».
- Вы создаете и обслуживаете критический CSS?
Чтобы гарантировать, что браузеры начнут отображать вашу страницу как можно быстрее, стало обычной практикой собирать весь CSS, необходимый для начала отображения первой видимой части страницы (известный как «критический CSS» или «CSS верхней части страницы»). ") и включить его в<head>
страницы, тем самым уменьшив круговые обращения. Из-за ограниченного размера пакетов, которыми обмениваются на этапе медленного старта, ваш бюджет на критический CSS составляет около 14 КБ.Если вы пойдете дальше этого, браузеру потребуются дополнительные обращения, чтобы получить больше стилей. CriticalCSS и Critical позволяют выводить критический CSS для каждого используемого вами шаблона. Однако, по нашему опыту, никакая автоматическая система не была лучше, чем ручной сбор критически важных CSS для каждого шаблона, и действительно, это подход, к которому мы недавно вернулись.
Затем вы можете встроить критический CSS и лениво загрузить остальные с помощью плагина critters Webpack. Если возможно, рассмотрите возможность использования подхода условного встраивания, используемого Filament Group, или преобразуйте встроенный код в статические ресурсы на лету.
Если вы в настоящее время загружаете свой полный CSS асинхронно с такими библиотеками, как loadCSS, в этом нет необходимости. С помощью
media="print"
вы можете заставить браузер асинхронно извлекать CSS, но применять его к среде экрана после его загрузки. ( спасибо, Скотт! )<!-- Via Scott Jehl. https://www.filamentgroup.com/lab/load-css-simpler/ --> <!-- Load CSS asynchronously, with low priority --> <link rel="stylesheet" href="full.css" media="print" onload="this.media='all'" />
При сборе всех важных CSS-файлов для каждого шаблона обычно исследуют только верхнюю часть страницы. Тем не менее, для сложных макетов может быть хорошей идеей включить основу макета, чтобы избежать огромных затрат на пересчет и перерисовку, что в результате повредит вашей оценке Core Web Vitals.
Что делать, если пользователь получает URL-адрес, ведущий прямо в середину страницы, но CSS еще не загружен? В этом случае стало обычным скрывать некритическое содержимое, например, с помощью
opacity: 0;
во встроенном CSS иopacity: 1
в полном файле CSS и отображать его, когда CSS доступен. Однако у него есть серьезный недостаток , поскольку пользователи с медленным соединением могут никогда не прочитать содержимое страницы. Вот почему лучше всегда держать содержимое видимым, даже если оно может быть оформлено неправильно.Помещение критического CSS (и других важных ресурсов) в отдельный файл в корневом домене имеет преимущества, иногда даже больше, чем встраивание, благодаря кэшированию. Chrome спекулятивно открывает второе HTTP-соединение с корневым доменом при запросе страницы, что устраняет необходимость в TCP-соединении для получения этого CSS. Это означает, что вы можете создать набор важных -CSS-файлов (например, crypto -homepage.css , crypto-product-page.css и т. д.) и обслуживать их из своего корня без необходимости встраивания. ( спасибо, Филипп! )
Небольшое предостережение: при использовании HTTP/2 важные CSS-коды можно хранить в отдельном CSS-файле и доставлять через сервер, не раздувая HTML-код. Загвоздка в том, что отправка на сервер была проблематичной из-за множества подводных камней и условий гонки в разных браузерах. Он никогда не поддерживался последовательно и имел некоторые проблемы с кэшированием (см. слайд 114 презентации Hooman Beheshti и далее).
На самом деле эффект может быть отрицательным и привести к раздуванию сетевых буферов, препятствуя доставке подлинных кадров в документе. Поэтому неудивительно, что на данный момент Chrome планирует отказаться от поддержки Server Push.
- Поэкспериментируйте с перегруппировкой правил CSS.
Мы привыкли к критическому CSS, но есть несколько оптимизаций, которые могли бы выйти за рамки этого. Гарри Робертс провел замечательное исследование с довольно неожиданными результатами. Например, было бы неплохо разделить основной файл CSS на отдельные медиа-запросы. Таким образом, браузер будет извлекать критический CSS с высоким приоритетом, а все остальное с низким приоритетом — полностью вне критического пути.Кроме того, избегайте размещения
<link rel="stylesheet" />
передasync
фрагментами. Если скрипты не зависят от таблиц стилей, рассмотрите возможность размещения блокирующих скриптов над блокирующими стилями. Если они это сделают, разделите этот JavaScript на две части и загрузите его по обе стороны от вашего CSS.Скотт Джел решил еще одну интересную проблему, кэшировав встроенный файл CSS с помощью сервис-воркера, — обычная проблема, знакомая тем, кто использует критически важный CSS. По сути, мы добавляем атрибут ID к элементу
style
, чтобы его было легко найти с помощью JavaScript, затем небольшой фрагмент JavaScript находит этот CSS и использует Cache API для его сохранения в локальном кеше браузера (с типом содержимогоtext/css
) для использования на последующих страницах. Чтобы избежать встраивания на последующих страницах и вместо этого ссылаться на кэшированные ресурсы извне, мы затем устанавливаем файл cookie при первом посещении сайта. Вуаля!Стоит отметить, что динамические стили тоже могут быть дорогими, но обычно только в тех случаях, когда вы полагаетесь на сотни одновременно отображаемых составных компонентов. Поэтому, если вы используете CSS-in-JS, убедитесь, что ваша библиотека CSS-in-JS оптимизирует выполнение, когда ваш CSS не зависит от темы или свойств, и не перекомпоновывайте стилизованные компоненты . Аггелос Арванитакис делится подробностями о затратах производительности на CSS-in-JS.
- Вы транслируете ответы?
Часто забываемые и игнорируемые потоки предоставляют интерфейс для чтения или записи асинхронных фрагментов данных, только часть которых может быть доступна в памяти в любой момент времени. По сути, они позволяют странице, отправившей первоначальный запрос, начать работу с ответом, как только станет доступен первый фрагмент данных, и используют синтаксические анализаторы, оптимизированные для потоковой передачи, для постепенного отображения контента.Мы могли бы создать один поток из нескольких источников. Например, вместо того, чтобы обслуживать пустую оболочку пользовательского интерфейса и позволять JavaScript заполнять ее, вы можете позволить сервис-воркеру создать поток, в котором оболочка поступает из кеша, а тело — из сети. Как заметил Джефф Посник, если ваше веб-приложение работает на базе CMS, которая отображает HTML-код на сервере путем сшивания частичных шаблонов, эта модель напрямую преобразуется в использование потоковых ответов, а логика шаблонов копируется в сервис-воркере, а не на вашем сервере. В статье Джейка Арчибальда «Год веб-потоков» рассказывается, как именно вы можете их построить. Прирост производительности весьма заметен.
Одним из важных преимуществ потоковой передачи всего HTML-ответа является то, что HTML-код, отображаемый во время начального запроса навигации, может в полной мере использовать преимущества потокового синтаксического анализатора HTML браузера. Фрагменты HTML, которые вставляются в документ после загрузки страницы (как это часто бывает с контентом, заполняемым с помощью JavaScript), не могут использовать преимущества этой оптимизации.
Поддержка браузера? По-прежнему достигается частичная поддержка в Chrome, Firefox, Safari и Edge с поддержкой API и Service Workers, которые поддерживаются во всех современных браузерах. И если вы снова чувствуете себя предприимчивым, вы можете проверить экспериментальную реализацию потоковых запросов, которая позволяет вам начать отправку запроса, продолжая генерировать тело. Доступно в Chrome 85.
- Подумайте о том, чтобы ваши компоненты знали о подключении.
Данные могут быть дорогими, и с ростом полезной нагрузки мы должны уважать пользователей, которые выбирают экономию данных при доступе к нашим сайтам или приложениям. Заголовок запроса подсказки клиента Save-Data позволяет нам настроить приложение и полезную нагрузку для пользователей с ограниченными затратами и производительностью.Фактически, вы можете переписать запросы для изображений с высоким DPI на изображения с низким DPI, удалить веб-шрифты, причудливые эффекты параллакса, миниатюры предварительного просмотра и бесконечную прокрутку, отключить автовоспроизведение видео, отправку сервером, уменьшить количество отображаемых элементов и понизить качество изображения или даже изменить способ доставки разметки. Тим Верике опубликовал очень подробную статью о стратегиях экономии данных, в которой представлено множество вариантов сохранения данных.
Кто использует
save-data
, вам может быть интересно? У 18 % пользователей Android Chrome по всему миру включен упрощенный режим (с включенным параметром «Save-Data
»), и, скорее всего, это число будет выше. Согласно исследованию Саймона Херна, самый высокий уровень подписки на более дешевых устройствах, но есть много исключений. Например: у пользователей в Канаде уровень отказа составляет более 34% (по сравнению с ~ 7% в США), а у пользователей последнего флагмана Samsung этот показатель составляет почти 18% во всем мире.При включенном режиме
Save-Data
Chrome Mobile обеспечивает оптимизированную работу, т. е. работу в Интернете через прокси с отложенными сценариями , принудительноеfont-display: swap
и принудительная отложенная загрузка. Просто более разумно создавать опыт самостоятельно, а не полагаться на браузер для выполнения этих оптимизаций.Заголовок в настоящее время поддерживается только в Chromium, в версии Chrome для Android или через расширение Data Saver на настольном устройстве. Наконец, вы также можете использовать API сетевой информации для доставки дорогостоящих модулей JavaScript, изображений и видео с высоким разрешением в зависимости от типа сети. Network Information API и, в частности,
navigator.connection.effectiveType
. EffectiveType используют значенияRTT
,downlink
,effectiveType
(и некоторые другие), чтобы обеспечить представление соединения и данных, которые могут обрабатывать пользователи.В этом контексте Макс Бок говорит о компонентах, поддерживающих подключение, а Эдди Османи говорит об адаптивном обслуживании модулей. Например, с помощью React мы могли бы написать компонент, который будет отображаться по-разному для разных типов подключения. Как предположил Макс, компонент
<Media />
в новостной статье может выводить:-
Offline
: заполнитель сalt
текстом, -
2G
/режимsave-data
: изображение с низким разрешением, -
3G
на экране без Retina: изображение среднего разрешения, -
3G
на экранах Retina: изображение Retina в высоком разрешении, -
4G
: HD-видео.
Дин Хьюм предлагает практическую реализацию аналогичной логики с помощью сервисного работника. Для видео мы могли бы отображать видеопостер по умолчанию, а затем отображать значок «Воспроизвести», а также оболочку видеоплеера, метаданные видео и т. д. при лучшем соединении. В качестве запасного варианта для неподдерживающих браузеров мы могли бы прослушивать событие
canplaythrough
и использоватьPromise.race()
для тайм-аута загрузки источника, если событиеcanplaythrough
не срабатывает в течение 2 секунд.Если вы хотите погрузиться немного глубже, вот несколько ресурсов для начала:
- Эдди Османи показывает, как реализовать адаптивное обслуживание в React.
- React Adaptive Loading Hooks & Utilities предоставляет фрагменты кода для React,
- Нетанель Базель исследует компоненты с поддержкой подключения в Angular,
- Теодор Ворилас рассказывает, как работает обслуживание адаптивных компонентов с помощью API сетевой информации в Vue.
- Умар Ханса показывает, как выборочно загружать/выполнять дорогостоящий JavaScript.
-
- Рассмотрите возможность того, чтобы ваши компоненты учитывали память устройства.
Однако сетевое подключение дает нам только одну перспективу в контексте пользователя. Идя дальше, вы также можете динамически настраивать ресурсы в зависимости от доступной памяти устройства с помощью API памяти устройства.navigator.deviceMemory
возвращает объем оперативной памяти устройства в гигабайтах, округленный до ближайшей степени двойки. В API также есть заголовок Client Hints,Device-Memory
, который сообщает то же значение.Бонус : Умар Ханса показывает, как отложить дорогостоящие сценарии с помощью динамического импорта, чтобы изменить работу в зависимости от памяти устройства, сетевого подключения и аппаратного параллелизма.
- Разогрейте соединение, чтобы ускорить доставку.
Используйте подсказки ресурсов, чтобы сэкономить время наdns-prefetch
(который выполняет поиск DNS в фоновом режиме),preconnect
(который просит браузер начать подтверждение соединения (DNS, TCP, TLS) в фоновом режиме),prefetch
(который запрашивает браузер для запроса ресурса) иpreload
(которая, среди прочего, выполняет предварительную выборку ресурсов без их выполнения). Хорошо поддерживается в современных браузерах, поддержка скоро появится в Firefox.Помните
prerender
? Подсказка ресурса, используемая для того, чтобы предложить браузеру построить всю страницу в фоновом режиме для следующей навигации. Проблемы с реализацией были довольно проблематичными, начиная от огромного объема памяти и использования полосы пропускания и заканчивая многочисленными зарегистрированными аналитическими обращениями и показами рекламы.Неудивительно, что он устарел, но команда Chrome вернула его как механизм NoState Prefetch. На самом деле, Chrome вместо этого обрабатывает подсказку
prerender
обработки как предварительную выборку NoState, поэтому мы все еще можем использовать ее сегодня. Как объясняет Кэти Хемпениус в этой статье, «как и предварительная отрисовка, NoState Prefetch извлекает ресурсы заранее , но, в отличие от предварительной отрисовки, она не выполняет JavaScript и не отображает какую-либо часть страницы заранее».Предварительная выборка NoState использует только ~45 МБ памяти, а извлекаемые подресурсы будут выбираться с приоритетом сети
IDLE
. Начиная с Chrome 69, NoState Prefetch добавляет заголовок Purpose: Prefetch ко всем запросам, чтобы их можно было отличить от обычного просмотра.Кроме того, следите за альтернативами предварительного рендеринга и порталами, новой попыткой предварительного рендеринга с учетом конфиденциальности, который обеспечит
preview
содержимого для плавной навигации.Использование подсказок ресурсов, вероятно , самый простой способ повысить производительность , и он действительно хорошо работает. Когда что использовать? Как объяснила Эдди Османи, разумно предварительно загружать ресурсы, которые, как мы знаем, с большой вероятностью будут использоваться на текущей странице и для будущих переходов через несколько границ навигации, например, пакеты Webpack, необходимые для страниц, которые пользователь еще не посещал.
В статье Эдди «Приоритеты загрузки в Chrome» показано, как именно Chrome интерпретирует подсказки ресурсов, поэтому, как только вы решили, какие ресурсы имеют решающее значение для рендеринга, вы можете назначить им высокий приоритет. Чтобы увидеть, как ваши запросы расставлены по приоритетам, вы можете включить столбец «приоритет» в таблице сетевых запросов Chrome DevTools (а также Safari).
Большую часть времени в эти дни мы будем использовать по крайней мере
preconnect
иdns-prefetch
, и мы будем осторожны с использованиемprefetch
,preload
иprerender
. Обратите внимание, что даже сpreconnect
иdns-prefetch
у браузера есть ограничение на количество хостов, к которым он будет искать/подключаться параллельно, поэтому можно с уверенностью упорядочить их на основе приоритета ( спасибо Филиппу Теллису! ).Поскольку шрифты обычно являются важными активами на странице, иногда рекомендуется запросить у браузера загрузку важных шрифтов с
preload
загрузкой. Тем не менее, дважды проверьте, действительно ли это помогает производительности, поскольку при предварительной загрузке шрифтов существует загадка приоритетов: посколькуpreload
считается очень важной, она может обойти даже более важные ресурсы, такие как критический CSS. ( спасибо, Барри! )<!-- Loading two rendering-critical fonts, but not all their weights. --> <!-- crossorigin="anonymous" is required due to CORS. Without it, preloaded fonts will be ignored. https://github.com/w3c/preload/issues/32 via https://twitter.com/iamakulov/status/1275790151642423303 --> <link rel="preload" as="font" href="Elena-Regular.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" /> <link rel="preload" as="font" href="Mija-Bold.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" />
<!-- Loading two rendering-critical fonts, but not all their weights. --> <!-- crossorigin="anonymous" is required due to CORS. Without it, preloaded fonts will be ignored. https://github.com/w3c/preload/issues/32 via https://twitter.com/iamakulov/status/1275790151642423303 --> <link rel="preload" as="font" href="Elena-Regular.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" /> <link rel="preload" as="font" href="Mija-Bold.woff2" type="font/woff2" crossorigin="anonymous" media="only screen and (min-width: 48rem)" />
Поскольку
<link rel="preload">
принимает атрибутmedia
, вы можете выбрать выборочную загрузку ресурсов на основе правил запроса@media
, как показано выше.Кроме того, мы можем использовать
imagesrcset
иimagesizes
для более быстрой предварительной загрузки недавно обнаруженных главных изображений или любых изображений, загружаемых через JavaScript, например постеры фильмов:<!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="image" href="poster.jpg" image image>
<!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="image" href="poster.jpg" image image>
Мы также можем предварительно загрузить JSON как fetch , чтобы он был обнаружен до того, как JavaScript запросит его:
<!-- Addy Osmani. https://addyosmani.com/blog/preload-hero-images/ --> <link rel="preload" as="fetch" href="foo.com/api/movies.json" crossorigin>
Мы также можем динамически загружать JavaScript, что эффективно для ленивого выполнения скрипта.
/* Adding a preload hint to the head */ var preload = document.createElement("link"); link.href = "myscript.js"; link.rel = "preload"; link.as = "script"; document.head.appendChild(link); /* Injecting a script when we want it to execute */ var script = document.createElement("script"); script.src = "myscript.js"; document.body.appendChild(script);
Следует иметь в виду несколько нюансов:
preload
загрузка хороша для перемещения времени начала загрузки ресурса ближе к исходному запросу, но предварительно загруженные ресурсы попадают в кеш памяти , привязанный к странице, делающей запрос.preload
хорошо сочетается с кешем HTTP: сетевой запрос никогда не отправляется, если элемент уже находится в кеше HTTP.Следовательно, это полезно для недавно обнаруженных ресурсов, основных изображений, загружаемых через
background-image
, встраивания критически важного CSS (или JavaScript) и предварительной загрузки остальной части CSS (или JavaScript).Тег
preload
может инициировать предварительную загрузку только после того, как браузер получит HTML-код с сервера и упреждающий синтаксический анализатор найдет тегpreload
. Предварительная загрузка через заголовок HTTP может быть немного быстрее, поскольку нам не нужно ждать, пока браузер проанализирует HTML, чтобы начать запрос (хотя это обсуждается).Ранние подсказки помогут еще больше, позволяя выполнять предварительную загрузку еще до того, как будут отправлены заголовки ответов для HTML (на дорожной карте в Chromium, Firefox). Кроме того, подсказки по приоритетам помогут нам указать приоритеты загрузки для скриптов.
Остерегайтесь : если вы используете
preload
,as
должно быть определено, иначе ничего не загружается, а также предварительно загруженные шрифты без атрибутаcrossorigin
будут выполнять двойную выборку. Если вы используетеprefetch
, остерегайтесь проблем с заголовкомAge
в Firefox.
- Используйте сервис-воркеры для кэширования и отката сети.
Никакая оптимизация производительности по сети не может быть быстрее, чем локально сохраненный кеш на компьютере пользователя (хотя есть и исключения). Если ваш веб-сайт работает по протоколу HTTPS, мы можем кэшировать статические ресурсы в кеше сервис-воркера и хранить автономные запасные варианты (или даже автономные страницы) и извлекать их с компьютера пользователя, а не обращаться в сеть.Как предложил Фил Уолтон, с помощью сервис-воркеров мы можем отправлять меньшие полезные данные HTML, программно генерируя наши ответы. Сервисный работник может запросить с сервера лишь минимальный объем необходимых ему данных (например, частичный HTML-контент, файл Markdown, данные JSON и т. д.), а затем программно преобразовать эти данные в полный HTML-документ. Таким образом, как только пользователь посещает сайт и сервис-воркер установлен, пользователь больше никогда не будет запрашивать полную HTML-страницу. Влияние на производительность может быть весьма впечатляющим.
Поддержка браузера? Сервисные работники широко поддерживаются, и в любом случае запасным вариантом является сеть. Помогает ли это повысить производительность ? О да, это так. И это становится лучше, например, с фоновой загрузкой, позволяющей фоновую загрузку/выгрузку также через работника службы.
Существует несколько вариантов использования сервис-воркера. Например, вы можете реализовать функцию «Сохранить для автономного режима», обрабатывать поврежденные изображения, ввести обмен сообщениями между вкладками или предоставить различные стратегии кэширования в зависимости от типов запросов. В общем, распространенная надежная стратегия заключается в том, чтобы хранить оболочку приложения в кэше сервис-воркера вместе с несколькими важными страницами, такими как автономная страница, главная страница и все остальное, что может быть важно в вашем случае.
Однако есть несколько ошибок, о которых следует помнить. С сервис-воркером нам нужно остерегаться запросов диапазона в Safari (если вы используете Workbox для сервис-воркера, у него есть модуль запроса диапазона). Если вы когда-нибудь сталкивались с
DOMException: Quota exceeded.
ошибка в консоли браузера, затем загляните в статью Джерардо Когда 7 КБ равно 7 МБ.Как пишет Джерардо: «Если вы создаете прогрессивное веб-приложение и сталкиваетесь с раздутым хранилищем кеша, когда ваш сервисный работник кэширует статические ресурсы, обслуживаемые из CDN, убедитесь, что для ресурсов из разных источников существует правильный заголовок ответа CORS, вы не кэшируете непрозрачные ответы. с вашим сервис-воркером вы непреднамеренно включаете ресурсы изображения из разных источников в режим CORS, добавляя атрибут
crossorigin
к тегу<img>
».Существует множество отличных ресурсов для начала работы с сервис-воркерами:
- Мышление сервисного работника, которое поможет вам понять, как сервисные работники работают за кулисами, и что нужно понимать при их создании.
- Крис Фердинанди (Chris Ferdinandi) опубликовал большую серию статей о сервис-воркерах, в которых объясняется, как создавать автономные приложения, и рассматриваются различные сценарии, от сохранения недавно просмотренных страниц в автономном режиме до установки даты истечения срока действия для элементов в кэше сервис-воркеров.
- Подводные камни сервис-воркеров и передовой опыт, а также несколько советов о сфере применения, отсрочке регистрации сервис-воркера и кэшировании сервис-воркера.
- Отличная серия статей Айра Адеринокуна «Сначала в автономном режиме» с Service Worker и стратегией предварительного кэширования оболочки приложения.
- Service Worker: Введение с практическими советами о том, как использовать Service Worker для многофункционального автономного взаимодействия, периодической фоновой синхронизации и push-уведомлений.
- Всегда стоит обратиться к старой доброй оффлайн-поваренной книге Джейка Арчибальда с рядом рецептов, как испечь собственного сервис-воркера.
- Workbox — это набор библиотек сервис-воркеров, созданных специально для создания прогрессивных веб-приложений.
- Запускаете ли вы рабочих серверов на CDN/Edge, например, для A/B-тестирования?
На данный момент мы уже привыкли запускать сервис-воркеры на клиенте, но с CDN, реализующими их на сервере, мы могли бы использовать их для настройки производительности и на периферии.Например, в A/B-тестах, когда HTML нужно менять содержимое для разных пользователей, мы могли бы использовать Service Workers на серверах CDN для обработки логики. Мы также могли бы транслировать переписывание HTML для ускорения работы сайтов, использующих Google Fonts.
- Оптимизация производительности рендеринга.
Всякий раз, когда приложение тормозит, это сразу заметно. Поэтому нам нужно убедиться, что при прокрутке страницы или при анимации элемента нет задержек, и что вы постоянно набираете 60 кадров в секунду. Если это невозможно, то, по крайней мере, сделать количество кадров в секунду согласованным предпочтительнее, чем смешанный диапазон от 60 до 15. Используйте CSSwill-change
, чтобы сообщить браузеру, какие элементы и свойства будут изменены.Всякий раз, когда вы испытываете, отлаживайте ненужные перерисовки в DevTools:
- Измеряйте производительность рендеринга во время выполнения. Ознакомьтесь с некоторыми полезными советами о том, как это понять.
- Для начала ознакомьтесь с бесплатным курсом Udacity Пола Льюиса по оптимизации рендеринга браузера и статьей Георгия Марчука «Отрисовка браузера и рекомендации по веб-производительности».
- Включите Paint Flashing в «Дополнительные инструменты → Рендеринг → Paint Flashing» в Firefox DevTools.
- В React DevTools установите флажок «Выделять обновления» и включите «Записывать, почему отображается каждый компонент».
- Вы также можете использовать Why Did You Render, чтобы при повторном рендеринге компонента вспышка уведомляла вас об изменении.
Вы используете макет Masonry? Имейте в виду, что очень скоро можно будет создать макет Masonry только с сеткой CSS.
Если вы хотите глубже погрузиться в тему, Нолан Лоусон поделился в своей статье приемами точного измерения производительности макета, а Джейсон Миллер также предложил альтернативные методы. У нас также есть небольшая статья Сергея Чикуёнка о том, как правильно сделать анимацию на GPU.
Примечание : изменения в слоях, созданных с помощью графического процессора, являются наименее затратными, поэтому, если вы можете уйти, запустив только композитинг с помощью
opacity
иtransform
, вы будете на правильном пути. Анна Мигас также дала много практических советов в своем выступлении на тему «Отладка производительности рендеринга пользовательского интерфейса». А чтобы понять, как отладить производительность рисования в DevTools, посмотрите видео аудита производительности рисования Умара. - Оптимизировали ли вы воспринимаемую производительность?
Хотя последовательность того, как компоненты отображаются на странице, и стратегия того, как мы обслуживаем ресурсы для браузера, имеют значение, мы также не должны недооценивать роль воспринимаемой производительности. Концепция касается психологических аспектов ожидания, в основном удерживая клиентов занятыми или вовлеченными, пока что-то еще происходит. Вот где в игру вступают управление восприятием, опережающее начало, раннее завершение и управление толерантностью.Что все это значит? При загрузке активов мы можем стараться всегда быть на шаг впереди клиента, поэтому работа кажется быстрой, в то время как в фоновом режиме происходит довольно много. Чтобы поддерживать интерес клиента, мы можем тестировать скелетные экраны (демонстрация реализации) вместо загрузки индикаторов, добавлять переходы/анимацию и фактически обманывать UX, когда больше нечего оптимизировать.
В своем тематическом исследовании The Art of UI Skeletons Кумар Макмиллан делится некоторыми идеями и методами моделирования динамических списков, текста и конечного экрана, а также того, как учитывать скелетное мышление с помощью React.
Однако будьте осторожны: скелетные экраны следует протестировать перед развертыванием, поскольку некоторые тесты показали, что каркасные экраны могут работать хуже всего по всем показателям.
- Предотвращаете ли вы смену макета и перерисовку?
В области воспринимаемой производительности, вероятно, одним из наиболее разрушительных событий является смещение макета или перекомпоновка , вызванное масштабированием изображений и видео, веб-шрифтов, внедренной рекламы или поздно обнаруженными скриптами, которые заполняют компоненты реальным контентом. В результате покупатель может начать читать статью только для того, чтобы его прервал скачок макета над областью чтения. Опыт часто бывает резким и довольно дезориентирующим: и это, вероятно, случай загрузки приоритетов, которые необходимо пересмотреть.Сообщество разработало несколько методов и обходных путей, чтобы избежать перекомпоновки. Как правило, рекомендуется избегать вставки нового контента поверх существующего , если только это не происходит в ответ на действия пользователя. Всегда устанавливайте атрибуты ширины и высоты для изображений, чтобы современные браузеры выделяли поле и резервировали пространство по умолчанию (Firefox, Chrome).
Как для изображений, так и для видео мы можем использовать заполнитель SVG, чтобы зарезервировать поле отображения, в котором будет отображаться медиафайл. Это означает, что область будет зарезервирована должным образом, когда вам также потребуется сохранить ее соотношение сторон. Мы также можем использовать заполнители или резервные изображения для рекламы и динамического контента, а также предварительно выделять слоты макета.
Вместо отложенной загрузки изображений с помощью внешних скриптов рассмотрите возможность использования встроенной отложенной загрузки или гибридной отложенной загрузки, когда мы загружаем внешний скрипт только в том случае, если собственная отложенная загрузка не поддерживается.
Как упоминалось выше, всегда группируйте перерисовки веб-шрифтов и переходите от всех резервных шрифтов ко всем веб-шрифтам одновременно — просто убедитесь, что этот переход не слишком резкий, регулируя высоту строки и расстояние между шрифтами с помощью font-style-matcher. .
Чтобы переопределить метрики шрифта для резервного шрифта для эмуляции веб-шрифта, мы можем использовать дескрипторы @font-face для переопределения метрик шрифта (демонстрация, включена в Chrome 87). (Обратите внимание, что настройки сложны со сложными стеками шрифтов.)
Для поздних версий CSS мы можем убедиться, что важные для макета CSS встроены в заголовок каждого шаблона. Более того: для длинных страниц при добавлении вертикальной полосы прокрутки основной контент смещается на 16 пикселей влево. Чтобы отобразить полосу прокрутки раньше, мы можем добавить
overflow-y: scroll
html
, чтобы принудительно использовать полосу прокрутки при первой отрисовке. Последнее помогает, потому что полосы прокрутки могут вызвать нетривиальные сдвиги макета из-за перекомпоновки содержимого верхней части сгиба при изменении ширины. В основном это должно происходить на платформах с полосами прокрутки без наложения, такими как Windows. Но: ломаетposition: sticky
, потому что эти элементы никогда не будут прокручиваться из контейнера.Если вы имеете дело с заголовками, которые становятся фиксированными или прилипают к верхней части страницы при прокрутке, зарезервируйте место для заголовка, когда он становится сосновым, например, с помощью элемента-заполнителя или
margin-top
в содержимом. Исключением должны быть баннеры согласия на использование файлов cookie, которые не должны влиять на CLS, но иногда влияют: это зависит от реализации. В этой ветке Твиттера есть несколько интересных стратегий и выводов.Для компонента вкладки, который может включать различное количество текстов, вы можете предотвратить смену макета с помощью стеков сетки CSS. Помещая содержимое каждой вкладки в одну и ту же область сетки и скрывая одну из них за раз, мы можем гарантировать, что контейнер всегда будет занимать высоту большего элемента, поэтому не произойдет смещения макета.
Ну, и, конечно же, бесконечная прокрутка и «Загрузить еще» также могут вызвать сдвиг макета, если под списком есть контент (например, нижний колонтитул). Чтобы улучшить CLS, зарезервируйте достаточно места для содержимого, которое будет загружено до того, как пользователь прокрутит страницу до этой части страницы, удалите нижний колонтитул или любые элементы DOM внизу страницы, которые могут быть сдвинуты вниз при загрузке содержимого. выполнять предварительную выборку данных и изображений для контента, расположенного ниже сгиба, чтобы к моменту, когда пользователь прокручивает до конца, он уже был там. Вы также можете использовать библиотеки виртуализации списков, такие как react-window, для оптимизации длинных списков ( спасибо, Эдди Османи! ).
Чтобы убедиться, что влияние перекомпоновки ограничено, измерьте стабильность макета с помощью API нестабильности макета. С его помощью вы можете рассчитать показатель Cumulative Layout Shift ( CLS ) и включить его в качестве требования в свои тесты, чтобы всякий раз, когда появляется регрессия, вы могли отслеживать ее и исправлять.
Чтобы рассчитать оценку сдвига макета, браузер смотрит на размер области просмотра и перемещение нестабильных элементов в области просмотра между двумя визуализируемыми кадрами. В идеале оценка должна быть близка к
0
. Существует отличное руководство Милицы Михайлии и Филипа Уолтона о том, что такое CLS и как его измерить. Это хорошая отправная точка для измерения и поддержания предполагаемой производительности и предотвращения сбоев, особенно для критически важных бизнес-задач.Небольшой совет : чтобы узнать, что вызвало изменение макета в DevTools, вы можете изучить изменения макета в разделе «Опыт» на панели «Производительность».
Бонус : если вы хотите уменьшить количество перекомпоновок и перерисовок, ознакомьтесь с руководством Чариса Теодулу по минимизации перекомпоновки/перераспределения макета DOM и списком Пола Айриша «Что заставляет разметку/перекомпоновку», а также с CSSTriggers.com, справочной таблицей свойств CSS, которые запускают разметку, рисование. и композитинга.
Оглавление
- Подготовка: планирование и показатели
- Постановка реалистичных целей
- Определение среды
- Оптимизация активов
- Оптимизация сборки
- Оптимизация доставки
- Сеть, HTTP/2, HTTP/3
- Тестирование и мониторинг
- Быстрые победы
- Все на одной странице
- Скачать контрольный список (PDF, Apple Pages, MS Word)
- Подпишитесь на нашу рассылку по электронной почте, чтобы не пропустить следующие руководства.