Потребность в быстрых и отзывчивых приложениях как никогда высока в связи с переходом от настольных компьютеров к мобильным. При этом сложность и размер веб-приложений постоянно растут. Понятно, почему тема производительности веб-страниц сегодня актуальна
Эта статья дает базовое представление о тестировании производительности веб-сайтов в Playwright.
Содержание:
Скорость важна
Время, необходимое для продуктивной работы с онлайн-сервисом, влияет на восприятие пользователя. Полезные функции, великолепный дизайн и другие характеристики становятся неважными, если сервис работает медленно – и пользователи уходят.
Вы можете создать лучшее в мире приложение, но помните: у каждого пользователя есть ограниченное количество времени, которое он может потратить на решение своих проблем с помощью вашего сервиса. Превысив это количество, вы рискуете потерять пользователей, уступив другому, более производительному сервису. Особенно важна производительность для новых пользователей, которые еще не успели убедиться в качестве вашего сервиса.
БЕСПЛАТНО СКАЧАТЬ КНИГИ в телеграм канале Библиотека тестировщика
Конкурентное преимущество
У этого есть и светлая сторона: если низкая производительность может потопить онлайн-платформу, то высокая производительность вполне может помочь ей подняться в топ. Скорость и отзывчивость могут стать отличительной чертой сервиса, побуждая пользователей предпочесть его конкурентам. Поэтому инвестиции в эту область почти всегда окупаются. Некоторые примеры из практики известных компаний:
- Pinterest сократил время ожидания пользователей и увеличил трафик и конверсию.
- Компания Zalando применила небольшие меры по улучшению времени загрузки и обнаружила прямую корреляцию с увеличением дохода за сессию.
- BBC обнаружила, что каждая лишняя секунда загрузки страницы приводила к тому, что 10 % пользователей покидали ее.
Измерение производительности
Учитывая важность производительности, неслучайно браузеры предоставляют множество метрик производительности. Знание этих метрик с учетом времени обеспечит вам обратную связь, необходимую для поддержания производительности для ваших пользователей.
Для получения наиболее полной информации можно комбинировать несколько подходов:
- Мониторинг реальных пользователей позволяет понять, какую производительность испытывают конечные пользователи вашего сервиса.
- Синтетический мониторинг для проактивного сбора информации о производительности сервиса и обнаружения проблем до того, как с ними столкнутся пользователи.
- Собственно Тестирование производительности, чтобы избежать деградации производительности, на проде в первую очередь.
- Регулярные аудиты для получения оценки эффективности и предложений по ее улучшению, например, с помощью таких инструментов, как Google Lighthouse.
Метрики производительности Google’s Web Vitals
Поскольку Google стремится обеспечить более быстрый Интернет, метрики Web Vitals всегда должны быть в поле вашего зрения. Такие показатели, как Время до первого байта (TTFB), Общее время блокировки (TBT) или Первый значимый рисунок в контенте (FCP), являются хорошими индикаторами пользовательского опыта и заслуживают внимания.
Google рекомендует обратить внимание на три наиболее важных показателя: Скорость отрисовки самого большого элемента контента (LCP), Задержка до первого пользовательского ввода (FID) и Общий сдвиг разметки (CLS). Эти три показателя считаются основными метриками веб-страницы и дают хорошее представление о скорости ее загрузки, интерактивности и визуальной стабильности.
Но не все метрики Google Web Vitals подходят для синтетического мониторинга и тестирования производительности. Задержка до первого пользовательского ввода (FID) зависит от взаимодействия с пользователем, и ее лучше всего измерять с помощью мониторинга реальных пользователей. Используйте Общее время блокировки (TBT) в качестве метрики интерактивности при внутреннем тестировании в компании.
Оценка производительности веб-сайтов с помощью headless-инструментов
Как бы мы ни стремились создавать быстрые приложения, мы должны выполнять задачи по мониторингу и тестированию производительности, чтобы обеспечить непрерывную обратную связь и быстрое вмешательство в случае ухудшения метрик. Playwright предоставляет отличный набор инструментов для синтетического мониторинга и тестирования производительности:
- Доступ к API-интерфейсам Web Performance.
- При тестировании на Chromium – доступ к протоколу Chrome DevTools для проверки трафика, эмуляции сети и пр.
- Совместимость с библиотеками производительности из экосистемы Node.js.
API-интерфейсы Web Performance
Современные браузеры поддерживают множество API-интерфейсов Web Performance и Web Vitals.
API навигации и синхронизации ресурсов
API производительности Navigation Timing и Resource Timing являются спецификациями W3C. Документация MDN определяет область применения обоих:
Navigation timings – это метрики, измеряющие события навигации по странице в браузере. Resource timings – это подробные измерения сетевого времени загрузки ресурсов приложения. Оба предоставляют одинаковые свойства, доступные только для чтения, но Navigation timings измеряет тайминги главного документа, в то время как Resource timings измеряет время для всех ресурсов, запрашиваемых и загружаемых этим главным документом.
API Navigation Timing позволяет выводить метки ключевых событий на временнОй шкале загрузки страницы. Запись Navigation Timing включает такие метрики, как время отклика навигации, используемый протокол и время загрузки документа.
const { chromium } = require('playwright') ;(async () => { const browser = await chromium.launch() const page = await browser.newPage() await page.goto('https://danube-web.shop/') const navigationTimingJson = await page.evaluate(() => JSON.stringify(performance.getEntriesByType('navigation')) ) const navigationTiming = JSON.parse(navigationTimingJson) console.log(navigationTiming) await browser.close() })()
Консольный вывод
[{ name: 'https://danube-web.shop/', entryType: 'navigation', startTime: 0, duration: 1243.7999999998137, initiatorType: 'navigation', nextHopProtocol: 'http/1.1', workerStart: 0, redirectStart: 0, redirectEnd: 0, fetchStart: 0.10000000009313226, domainLookupStart: 1.2000000001862645, domainLookupEnd: 11.100000000093132, connectStart: 11.100000000093132, connectEnd: 336.8000000002794, secureConnectionStart: 102.89999999990687, requestStart: 336.89999999990687, responseStart: 432.39999999990687, responseEnd: 433.70000000018626, transferSize: 971, encodedBodySize: 671, decodedBodySize: 671, serverTiming: [], workerTiming: [], unloadEventStart: 0, unloadEventEnd: 0, domInteractive: 1128.8999999999069, domContentLoadedEventStart: 1128.8999999999069, domContentLoadedEventEnd: 1130.8999999999069, domComplete: 1235.3999999999069, loadEventStart: 1235.3999999999069, loadEventEnd: 1235.3999999999069, type: 'navigate', redirectCount: 0 }]
API Resource Timing позволяет рассмотреть внимательнее отдельные ресурсы и получить точную информацию о том, как быстро они загружаются. Например, логотип сайта:
const { chromium } = require('playwright') ;(async () => { const browser = await chromium.launch() const page = await browser.newPage() await page.goto('https://danube-web.shop/') const resourceTimingJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType('resource')) ) const resourceTiming = JSON.parse(resourceTimingJson) const logoResourceTiming = resourceTiming.find((element) => element.name.includes('.svg') ) console.log(logoResourceTiming) await browser.close() })()
Вывод в консоли:
{ name: 'https://danube-web.shop/static/logo-horizontal.svg', entryType: 'resource', startTime: 1149.1000000000931, duration: 96.89999999990687, initiatorType: 'img', nextHopProtocol: 'http/1.1', workerStart: 0, redirectStart: 0, redirectEnd: 0, fetchStart: 1149.1000000000931, domainLookupStart: 1149.1000000000931, domainLookupEnd: 1149.1000000000931, connectStart: 1149.1000000000931, connectEnd: 1149.1000000000931, secureConnectionStart: 1149.1000000000931, requestStart: 1149.6000000000931, responseStart: 1244.3000000002794, responseEnd: 1246, transferSize: 21049, encodedBodySize: 20749, decodedBodySize: 20749, serverTiming: [], workerTiming: [] }
API Времени отрисовки(first-paint
и first-contentful-paint
)
API Paint Timing предоставляет информацию о так называемых первом цвете и первом значимом цвете, здесь имеется в виду скорость отрисовки крупнейшего видимого элемента страницы в поле зрения. Доступ к записям осуществляется через performance.getEntriesByType('paint')
или performance.getEntriesByName('first-contentful-paint')
.
const { chromium } = require('playwright') ;(async () => { const browser = await chromium.launch() const page = await browser.newPage() await page.goto('https://danube-web.shop/') const paintTimingJson = await page.evaluate(() => JSON.stringify(window.performance.getEntriesByType('paint')) ) const paintTiming = JSON.parse(paintTimingJson) console.log(paintTiming) await browser.close() })()
[ { name: 'first-paint', entryType: 'paint', startTime: 1149.5, duration: 0 }, { name: 'first-contentful-paint', entryType: 'paint', startTime: 1149.5, duration: 0 } ]
API Contentful Paint(largest-contentful-paint
)
API Largest Contentful Paint предоставляет информацию о метриках скорости загрузки контента. Используйте этот API, чтобы оценить метрику Largest Contentful Paint (LCP) в Core Web Vital.
Отрисовка Contentful Paint – это не одно событие, а скорее потоки событий. За элементом всегда может последовать другой, более значимый. Для оценки LCP-метрик инициализируйте PerformanceObserver
, наблюдайте за largest-contentful-paint
и фиксируйте эти метрики.
const { chromium } = require('playwright') ;(async () => { const browser = await chromium.launch() const page = await browser.newPage() await page.goto('https://danube-web.shop/') const largestContentfulPaint = await page.evaluate(() => { return new Promise((resolve) => { new PerformanceObserver((l) => { const entries = l.getEntries() // the last entry is the largest contentful paint const largestPaintEntry = entries.at(-1) resolve(largestPaintEntry.startTime) }).observe({ type: 'largest-contentful-paint', buffered: true }) }) }) console.log(parseFloat(largestContentfulPaint)) // 1139.39 await browser.close() })()
API Нестабильности разметки (layout-shift
)
API нестабильности разметки предоставляет информацию обо всех сдвигах разметки (макета). Используйте этот API для оценки так называемого Общего сдвига разметки (CLS) в Core Web Vital.
Сдвиги разметки – не одно событие, а поток событий. Для вычисления CLS инициализируйте PerformanceObserver
, наблюдайте за записями layout-shift
и суммируйте все сдвиги.
const { chromium } = require('playwright') ;(async () => { const browser = await chromium.launch() const page = await browser.newPage() await page.goto('https://danube-web.shop/') const cummulativeLayoutShift = await page.evaluate(() => { return new Promise((resolve) => { let CLS = 0 new PerformanceObserver((l) => { const entries = l.getEntries() entries.forEach(entry => { if (!entry.hadRecentInput) { CLS += entry.value } }) resolve(CLS) }).observe({ type: 'layout-shift', buffered: true }) }) }) console.log(parseFloat(cummulativeLayoutShift)) // 0.0001672498 await browser.close() })()
Long Task API(longtask
)
API Long Task предоставляет информацию обо всех выполнениях JavaScript, занимающих 50 миллисекунд и более. Используйте этот API для оценки метрики Total Blocking Time (TBT) в Web Vital.
“Длинные задачи” – это, как и в предыдущих примерах, не отдельные события, а потоки событий. Для расчета TBT инициализируйте PerformanceObserver
, наблюдайте за записями longtasks
и суммируйте события с временем выполнения JavaScript в 50 миллисекунд и более.
const { chromium } = require('playwright') ;(async () => { const browser = await chromium.launch() const page = await browser.newPage() await page.goto('https://danube-web.shop/') const totalBlockingTime = await page.evaluate(() => { return new Promise((resolve) => { let totalBlockingTime = 0 new PerformanceObserver(function (list) { const perfEntries = list.getEntries() for (const perfEntry of perfEntries) { totalBlockingTime += perfEntry.duration - 50 } resolve(totalBlockingTime) }).observe({ type: 'longtask', buffered: true }) // Resolve promise if there haven't been long tasks setTimeout(() => resolve(totalBlockingTime), 5000) }) }) console.log(parseFloat(totalBlockingTime)) // 0 await browser.close() })()
Инструменты производительности Chrome DevTools
Если API производительности браузера недостаточно, то протокол Chrome DevTools предлагает множество инструментов производительности, которые мы можем использовать в Playwright.
Одним из важных примеров является троттлинг сети, с помощью которого мы можем имитировать работу пользователей, заходящих на нашу страницу в различных сетевых условиях.
const { chromium } = require('playwright') ;(async () => { const browser = await chromium.launch() const page = await browser.newPage() const client = await page.context().newCDPSession(page) await client.send('Network.enable') await client.send('Network.emulateNetworkConditions', { offline: false, downloadThroughput: (4 * 1024 * 1024) / 8, uploadThroughput: (3 * 1024 * 1024) / 8, latency: 20 }) await page.goto('https://danube-web.shop/') // your flow and assertions await browser.close() })()
Протокол DevTools весьма обширен. Мы рекомендуем изучить его документацию, чтобы получить полное представление о его возможностях.
Дополнительные библиотеки производительности
Также можно использовать Lighthouse с Playwright, для сбора метрик, таких как Time To Interactive (TTI):
const chromeLauncher = require('chrome-launcher') const { chromium } = require('playwright') const lighthouse = require('lighthouse') const request = require('request') const util = require('util') ;(async () => { const chrome = await chromeLauncher.launch() const resp = await util.promisify(request)( `http://localhost:${chrome.port}/json/version` ) const { webSocketDebuggerUrl } = JSON.parse(resp.body) const browser = await chromium.connect({ wsEndpoint: webSocketDebuggerUrl }) const { lhr } = await lighthouse( 'https://danube-web.shop/', { port: chrome.port }, null ) console.log('Report complete for', lhr.finalUrl) console.log(` Time To Interactive - Score: ${lhr.audits.interactive.score}, Value: ${lhr.audits.interactive.numericValue} ${lhr.audits.interactive.numericUnit} `) await browser.close() await chrome.kill() })()
Все приведенные выше примеры можно выполнить следующим образом:
$ node measure-performance.js
Перевод статьи «Measuring Page Performance Using Playwright – Best Practices».