Современные веб-сайты — это сложная мозаика из собственных и сторонних ресурсов. Шрифты, оптимизированные медиафайлы, скрипты аналитики и рекламы — большинство сайтов включают внешние зависимости, которые не находятся под их прямым контролем.
И хотя с точки зрения производительности всегда лучше размещать ресурсы самостоятельно, избежать внешних файлов часто невозможно. Но будьте бдительны: использование стороннего кода может привести к серьезным проблемам.
Внешние ресурсы нередко интегрированы слишком тесно или некорректно. Например, блокировщики рекламы могут заблокировать критичный скрипт, или у внешнего поставщика произойдет сбой. Оба сценария могут вызвать простой фронтенда, ухудшить пользовательский опыт (UX) или полностью сломать важный функционал.
Как технический руководитель в сфере мониторинга, я считаю, что критически важно не просто тестировать, а мониторить отказы внешних ресурсов.
Давайте рассмотрим примеры того, как сторонний код может навредить, и разберем, как использовать Playwright для мониторинга вашего стека, чтобы внешние провайдеры или блокировщики не создавали проблем для вашего бизнеса. 🫡
Паттерны, которые ломают сайты из-за внешних ресурсов
Основываясь на моем опыте, можно выделить два основных сценария, создающих наибольшие трудности.
1. Медленные и блокирующие рендеринг ресурсы
Много лет назад я работал в e-commerce, и мы использовали внешнего провайдера для рекламных баннеров. Это было быстрое решение для сбора статистики кликов. Баннеры внедрялись с помощью синхронных <script> элементов в верхней части страницы (above the fold):
<script type="text/javascript" src="http://some-provider.js"></script>Синхронный скрипт останавливает парсер HTML и заставляет браузер ждать его загрузки и выполнения. Рендеринг возобновляется только после завершения работы скрипта.
Проблема возникла, когда внешний поставщик столкнулся с сбоем: запросы к его скрипту не завершались ошибкой, а просто зависали и в итоге таймаутили. В результате браузер начинал рендерить страницу, доходил до слайд-шоу, останавливался и ждал 10-20 секунд, пока скрипт не вернет ошибку по таймауту. В это время наши клиенты видели белый экран. Фактически, внешний поставщик обрушил фронтенд из-за некорректной реализации синхронного скрипта.
Вывод: Каждая внешняя зависимость — это риск. Избегайте критических, блокирующих рендеринг элементов. Проверяйте, что произойдет, если эти ресурсы перестанут работать или сильно замедлятся.
Проблема возникла, когда внешний поставщик столкнулся с сбоем: запросы к его скрипту не завершались ошибкой, а просто зависали и в итоге таймаутили. В результате браузер начинал рендерить страницу, доходил до слайд-шоу, останавливался и ждал 10-20 секунд, пока скрипт не вернет ошибку по таймауту. В это время наши клиенты видели белый экран. Фактически, внешний поставщик обрушил фронтенд из-за некорректной реализации синхронного скрипта.
Вывод: Каждая внешняя зависимость — это риск. Избегайте критических, блокирующих рендеринг элементов. Проверяйте, что произойдет, если эти ресурсы перестанут работать или сильно замедлятся.
2. Тесно связанные JavaScript-зависимости
Многие разработчики сталкиваются с тем, что их критически важные функции (например, оформление заказа) ломаются из-за выброшенного JavaScript-исключения. Это часто происходит из-за блокировщиков рекламы.
Блокировщик выполняет свою работу — блокирует трекеры (внешние скрипты). Но проблема в том, что ваш собственный код может ожидать наличия этих скриптов. Например, если ваш JavaScript полагается на доступность функции sendTracking в глобальном объекте window, блокировщик, который ее удаляет или не дает загрузить, может спровоцировать сбой.
Вот простой пример:
// tracking-script.js:
window.trackThing = function() { /* report data */ }
// /script/index.js:
function init() {
// ЭТО вызовет исключение, если `tracking-script.js` был заблокирован!
trackThing("page loaded or something");
}
init();Если tracking-script.js заблокирован, ваш код выбрасывает исключение и может "уронить" все приложение. При внедрении внешнего JavaScript-функционала всегда необходимо убедиться, что система остается работоспособной даже в случае ошибок. Обработка заказа важнее, чем его отслеживание.
Как тестировать и мониторить сторонние ресурсы с Playwright
Playwright — идеальный инструмент для имитации сетевых условий и проверки устойчивости вашего приложения. В Pingera мы используем его для синтетических проверок, которые позволяют проактивно выявлять такие проблемы.
Эмуляция медленных сторонних ресурсов
Используя метод page.route, мы можем перехватить запросы и искусственно замедлить их. Это позволяет оценить влияние внешних задержек на метрики производительности, такие как Largest Contentful Paint (LCP).
Сценарий Playwright:
Мы создаем простой тест, который замедляет все запросы к внешним ресурсам на 10 секунд.
const { test, expect } = require("@playwright/test")
test("тест с медленными ресурсами", async ({ page }) => {
// Перехватываем все запросы
page.route(
"**",
(route) =>
new Promise((resolve) => {
const requestURL = route.request().url()
// Разрешаем запросы к собственному домену мгновенно
if (requestURL.match(/http:\/\/localhost:8080/)) {
resolve(route.continue())
} else {
// Задерживаем внешние запросы на 10 секунд
setTimeout(() => {
console.log(`Задержка: ${requestURL}`)
resolve(route.continue())
}, 10000)
}
})
)
await page.goto("http://localhost:8080")
// Оценка LCP (Largest Contentful Paint)
const largestContentfulPaint = await page.evaluate(() => {
return new Promise((resolve) => {
new PerformanceObserver((l) => {
const entries = l.getEntries()
const largestPaintEntry = entries.at(-1)
resolve(largestPaintEntry.startTime)
}).observe({
type: "largest-contentful-paint",
buffered: true,
})
})
})
console.log(`LCP: ${parseFloat(largestContentfulPaint)}мс`)
await expect(page).toHaveTitle(/Frontend downtime/)
})Результат:
- Время выполнения теста и LCP увеличиваются до ∼10 секунд.
- Мы видим, что внешний ресурс, даже если он размещен внизу страницы, может сильно замедлить событие onload, на котором Playwright по умолчанию завершает page.goto, и что более важно — заблокировать рендеринг страницы для пользователя на 10 секунд.
Блокировка внешних ресурсов и отслеживание ошибок
Чтобы имитировать поведение Ad Blocker, мы можем полностью блокировать запросы к внешним доменам с помощью page.route и метода route.abort(). При этом мы мониторим ошибки страницы с помощью события page.on("pageerror").
test("тест с заблокированными сторонними ресурсами", async ({ page }) => {
page.route("**", (route) => {
const requestURL = route.request().url()
// Разрешаем запросы к собственному домену
if (requestURL.match(/http:\/\/localhost:8080/)) {
route.continue()
} else {
// Блокируем все остальные (внешние)
route.abort()
}
})
// Мониторим выброшенные на странице ошибки JavaScript
const errors = []
page.on("pageerror", (error) => {
errors.push(error)
})
await page.goto("http://localhost:8080")
// Здесь мы проверяем, что в массиве ошибок нет никаких исключений
// Иначе тест провалится!
expect(errors.length).toBe(0)
})Результат: Если ваш встроенный JavaScript ожидает загрузки заблокированного скрипта и пытается вызвать из него несуществующую функцию, тест провалится, а в консоли вы увидите: ReferenceError: bootstrap is not defined (или любую другую ошибку, связанную с отсутствием внешней зависимости).
Таким образом, вы можете выполнять сквозные тесты (end-to-end) и убеждаться, что ваш основной функционал не зависит от наличия или корректной работы сторонних ресурсов.
Тестирование vs. Мониторинг
Тестирование с ограничениями ресурсов на этапе развертывания — отличный первый шаг. Оно гарантирует, что ваш код устойчив к медленным или недоступным зависимостям.
Однако это лишь первый шаг. E2E-тестирование проверяет работоспособность в один момент времени. Если вы полагаетесь на сторонние ресурсы, их простой может повлиять на ваш сайт уже завтра, после успешного деплоя.
Именно поэтому важно переходить от тестирования к мониторингу. Запускайте ваши Playwright-сценарии с измененными сетевыми настройками по расписанию, чтобы гарантировать, что ваш сайт и его внешние зависимости работают без сбоев 24/7. Будьте первым, кто узнает о проблемах у ваших поставщиков!
Заключение
- Проверяйте влияние: Если вы используете внешние ресурсы, узнайте, как их недоступность или замедление повлияет на ваш фронтенд. Отдавайте предпочтение self-хостингу, а если это невозможно — асинхронной и отказоустойчивой реализации.
- Тестируйте устойчивость: Используйте page.route в Playwright, чтобы имитировать медленную работу сети или блокировку ресурсов. Убедитесь, что ваш критический функционал продолжает работать.
- Мониторьте постоянно: Не полагайтесь только на тесты при деплое. Используйте Синтетические проверки Pingera, чтобы запускать ваши Playwright-сценарии по расписанию и оперативно выявлять любые проблемы.
Не повторяйте мою ошибку, когда пользователи часами смотрели на белый экран. Возьмите полный контроль над своими внешними зависимостями.