В Chrome 102 вы увидите новую экспериментальную панель « Performance Insights » в инструментах разработчика. В этой публикации мы обсудим не только причины, побудившие нас работать над новой панелью, но и технические проблемы, с которыми мы столкнулись, и принятые нами решения.
Зачем строить еще одну панель?
(Если вы еще не видели, мы опубликовали видео о том, зачем нужна панель «Статистика производительности» и как с ее помощью можно получить полезную информацию об эффективности вашего веб-сайта.)
Существующая панель производительности — отличный ресурс, если вы хотите видеть все данные по вашему сайту в одном месте, но мы посчитали, что она может быть немного перегружена. Если вы не эксперт по производительности, вам сложно понять, на что именно обращать внимание и какие части записи важны.
Перейдите на панель «Статистика», где вы по-прежнему можете просматривать временную шкалу трассировки и анализировать данные, а также получить удобный список того, что DevTools считает основными «Статистиками», заслуживающими внимания. «Статистика» выявит такие проблемы, как запросы на блокировку рендеринга, сдвиги макета и длительные задачи, и это лишь некоторые из них, которые могут негативно повлиять на скорость загрузки страниц вашего сайта и, в частности, на его показатели Core Web Vital (CWV) . Помимо выявления проблем, «Статистика производительности» предоставит вам практические рекомендации по улучшению показателей CWV и ссылки на дополнительные ресурсы и документацию.
Эта панель экспериментальная, и нам важно ваше мнение! Пожалуйста, сообщите нам, если вы обнаружите какие-либо ошибки или у вас есть пожелания по функциям, которые, по вашему мнению, помогут вам улучшить производительность вашего сайта.
Как мы создали Performance Insights
Как и остальные инструменты разработчика, мы создали Performance Insights на TypeScript и использовали веб-компоненты на основе lit-html для создания пользовательского интерфейса. Отличие Performance Insights заключается в том, что основной пользовательский интерфейс представляет собой HTML-элемент canvas
, на котором отображается временная шкала. Значительная часть сложности возникает при управлении этим холстом: не только в отрисовке нужных деталей в нужных местах, но и в управлении событиями мыши (например: где пользователь нажал на холсте? Нажал ли он на событие, которое мы нарисовали?), а также в обеспечении эффективной перерисовки холста.
Несколько дорожек на одном холсте
Для данного веб-сайта существует несколько «треков», которые мы хотим отобразить, каждый из которых представляет отдельную категорию данных. Например, на панели «Статистика» по умолчанию будут отображаться три трека:
И по мере того, как мы продолжаем размещать функции на панели, мы ожидаем, что будет добавлено больше треков.
Изначально мы хотели, чтобы каждый из этих треков отображался <canvas>
, так что основное представление представляло бы собой несколько элементов холста, расположенных вертикально друг над другом. Это упростило бы рендеринг на уровне треков, поскольку каждый трек мог бы отображаться изолированно, и не было бы опасности выхода трека за его пределы. Но, к сожалению, такой подход имеет два серьёзных недостатка:
(Повторный) рендеринг элементов canvas
требует больших затрат; использование нескольких холстов обходится дороже, чем одного, даже если он больше. Рендеринг любых наложений, проходящих через несколько дорожек (например, вертикальных линий для обозначения событий, таких как время FCP), становится сложным: необходимо рендерить их на нескольких холстах и обеспечивать их совместную визуализацию и правильное выравнивание.
Использование одного canvas
для всего пользовательского интерфейса означало, что нам нужно было решить, как обеспечить рендеринг каждой дорожки в правильных координатах и избежать перекрытия другой дорожки. Например, если высота определённой дорожки составляет 100 пикселей, мы не можем позволить ей рендерить что-то высотой 120 пикселей и заливать им дорожку, расположенную ниже. Для решения этой проблемы мы можем использовать clip
. Перед рендерингом каждой дорожки мы рисуем прямоугольник, представляющий видимое окно дорожки. Это гарантирует, что любые пути, нарисованные за пределами этих границ, будут обрезаны холстом.
canvasContext.beginPath();
canvasContext.rect(
trackVisibleWindow.x, trackVisibleWindow.y, trackVisibleWindow.width, trackVisibleWindow.height);
canvasContext.clip();
Мы также не хотели, чтобы каждая дорожка знала своё положение по вертикали: каждая дорожка должна визуализироваться так, как будто она отображается в точке (0, 0), и у нас есть компонент более высокого уровня (который мы называем TrackManager
) для управления общим положением дорожки. Это можно сделать с помощью translate
, который перемещает холст на заданную позицию (x, y). Например:
canvasContext.translate(0, 10); // Translate by 10px in the y direction
canvasContext.rect(0, 0, 10, 10); // draw a rectangle at (0, 0) that’s 10px high and wide
Несмотря на то, что код rect
задаёт позицию 0, 0
, применённое перемещение в целом приведёт к визуализации прямоугольника в точке 0, 10
Это позволяет нам работать с треками так, как будто мы выполняем визуализацию в точке (0, 0), и использовать менеджер треков для трансляции при визуализации каждого трека, чтобы гарантировать корректную визуализацию каждого трека под предыдущим.
Закадровые холсты для треков и лучших моментов
Рендеринг холста относительно затратен, и мы хотим, чтобы панель «Статистика» работала плавно и отзывчиво во время работы с ней. Иногда не избежать необходимости перерисовывать весь холст: например, при изменении масштаба приходится начинать всё заново и перерисовывать всё. Перерисовка холста особенно затратна, поскольку невозможно просто перерисовать небольшую его часть; необходимо очистить весь холст и перерисовать его заново. Это отличается от перерисовки DOM, где инструменты могут рассчитать минимально необходимый объём работы, а не удалять всё и начинать заново.
Одной из областей, где мы столкнулись с визуальными проблемами, было выделение. При наведении курсора на метрики на панели они выделяются на временной шкале, а при наведении курсора на аналитический отчет по определённому событию вокруг этого события появляется синяя рамка.
Эта функция была впервые реализована путём обнаружения наведения мыши на элемент, вызывающий подсветку, и последующего рисования этой подсветки непосредственно на основном холсте. Проблема возникает, когда нужно снять подсветку: единственный выход — перерисовать всё! Невозможно просто перерисовать выделенную область (не без значительных архитектурных изменений), но перерисовка всего холста только для того, чтобы убрать синюю рамку вокруг одного элемента, казалась излишней. Кроме того, при быстром наведении мыши на разные элементы, чтобы подсветить сразу несколько элементов, возникала визуальная задержка.
Чтобы исправить это, мы разделяем наш пользовательский интерфейс на два внеэкранных холста: «основной» холст, на котором рендерятся треки, и холст «подсветки», на котором рисуются подсветки. Затем мы рендерим, копируя эти холсты на единственный холст, видимый пользователю на экране. Мы можем использовать метод drawImage
в контексте холста, который может использовать другой холст в качестве источника.
Это означает, что снятие выделения не приводит к перерисовке основного холста: вместо этого мы можем очистить экранный холст, а затем скопировать основной холст на видимый. Сам процесс копирования холста обходится дёшево, а вот отрисовка — дорого; поэтому, перенося выделение на отдельный холст, мы избегаем этих затрат при включении и выключении выделения.
Комплексно протестированный анализ трассировки
Одно из преимуществ разработки новой функции с нуля заключается в возможности проанализировать принятые ранее технические решения и внести улучшения. Одним из наших улучшений было явное разделение кода на две практически полностью отдельные части:
Проанализируйте файл трассировки и извлеките необходимые данные. Создайте набор треков.
Разделение парсинга (часть 1) и работы с пользовательским интерфейсом (часть 2) позволило нам создать надёжную систему парсинга. Каждая трассировка проходит через ряд обработчиков , отвечающих за различные задачи: обработчик LayoutShiftHandler
вычисляет всю информацию, необходимую для смещений макета, а обработчик NetworkRequestsHandler
занимается исключительно извлечением сетевых запросов. Наличие этого явного этапа парсинга, где разные обработчики отвечают за разные части трассировки, также оказалось полезным: парсинг трассировки может быть очень сложным, и он помогает сосредоточиться на одной задаче за раз.
Мы также смогли всесторонне протестировать наш анализ трассировки, сделав записи в DevTools, сохранив их и загрузив в наш тестовый набор. Это очень удобно, поскольку мы можем проводить тестирование с реальными трассировками, а не накапливать огромные объёмы фальшивых данных, которые могут устареть.
Тестирование скриншотов для пользовательского интерфейса Canvas
Продолжая тему тестирования, мы обычно тестируем наши фронтенд-компоненты, отображая их в браузере и проверяя их поведение ожидаемым образом; мы можем отправлять события щелчков для запуска обновлений и проверять корректность DOM, генерируемого компонентами. Этот подход хорошо работает для нас, но не работает при рассмотрении рендеринга на холсте; нет способа проверить холст и определить, что на нём отрисовано! Поэтому наш обычный подход с рендерингом и последующим выполнением запросов не подходит.
Чтобы обеспечить некоторое покрытие тестами, мы обратились к тестированию с помощью скриншотов. Каждый тест запускает холст, отображает трек, который мы хотим протестировать, а затем делает снимок экрана элемента холста. Этот снимок экрана затем сохраняется в нашей кодовой базе, и последующие тестовые запуски будут сравнивать сохранённый снимок экрана с сгенерированным. Если снимки экрана отличаются, тест завершится неудачей. Мы также предоставляем флаг для запуска теста и принудительного обновления скриншота, когда мы намеренно изменили рендеринг и требуется обновить тест.
Скриншот-тесты неидеальны и несколько прямолинейны: можно проверить только корректность отрисовки всего компонента, а не более конкретных утверждений. Изначально мы злоупотребляли ими, чтобы гарантировать корректность отрисовки каждого компонента (HTML или холста). Это значительно замедлило наш набор тестов и привело к проблемам, когда небольшие, практически несущественные изменения пользовательского интерфейса (например, едва заметные изменения цвета или добавление отступов между элементами) приводили к сбоям в работе нескольких скриншотов и необходимости их обновления. Сейчас мы сократили использование скриншотов и используем их исключительно для компонентов на основе холста, и этот баланс пока нас вполне устраивает.
Заключение
Создание новой панели «Анализ производительности» стало для команды очень приятным и познавательным опытом. Мы узнали много нового о файлах трассировки, работе с Canvas и многом другом. Надеемся, вам понравится новая панель, и с нетерпением ждем ваших отзывов.
Дополнительную информацию о панели «Анализ эффективности» см. в статье Анализ эффективности: получите полезную информацию об эффективности вашего веб-сайта .
Загрузите предварительные версии каналов
Рассмотрите возможность использования Chrome Canary , Dev или Beta в качестве браузера для разработки по умолчанию. Эти предварительные версии предоставят вам доступ к новейшим функциям DevTools, позволят тестировать передовые API веб-платформ и помогут обнаружить проблемы на вашем сайте раньше, чем это сделают ваши пользователи!
Свяжитесь с командой Chrome DevTools
Используйте следующие параметры для обсуждения новых функций, обновлений или чего-либо еще, связанного с DevTools.
- Отправляйте отзывы и предложения по функциям нам на crbug.com .
- Сообщить о проблеме с DevTools можно с помощью функции Дополнительные параметры > Справка > Сообщить о проблеме с DevTools в DevTools.
- Напишите твит в @ChromeDevTools .
- Оставляйте комментарии в видеороликах YouTube «Что нового в DevTools» или YouTube «Советы по DevTools» .