TGStat
TGStat
Введите текст для поиска
Расширенный поиск каналов
  • Язык сайта
    flag Russian flag English flag Uzbek
  • Вход на сайт
  • Каталог
    Каталог каналов и чатов Региональные подборки Тематические подборки Платные каналы Поиск каналов
    Добавить канал/чат
  • Рейтинги
    Рейтинг каналов Рейтинг чатов Рейтинг публикаций
    Рейтинги брендов и персон
  • Аналитика
  • Поиск по публикациям
  • Мониторинг Telegram
  • Продвижение
    Реклама через Яндекс Бизнес Реклама в каналах через TGStat Agency Реклама на сайте TGStat.ru
@alexnozer_dev

26 Aug, 17:04

Открыть в Telegram Поделиться Пожаловаться

Не стоит полагаться на User Agent

User Agent sniffing — это техника поиска в строке User Agent названия браузера и его версии для определения того, с какого браузера пришёл пользователь. Это можно использовать во благо для оптимизаций и загрузки полифилов.

На практике это применялось для трекинга и фингерпринтинга. Поэтому браузеры уже давно пихают в User Agent всё подряд и врут. Поэтому User Agent sniffing — крайне ненадёжная и нестабильная штука. На днях я столкнулся с этим на практике.

При аудите производительности сайта во вкладке Performance я заметил много операций принудительной перекомпоновки (forced reflow). Это когда чтение и запись геометрии элементов в JS приводит к пересчёту стилей.

Виновником стал скрипт autosizes. Это полифил атрибута sizes="auto" у и . Он запрашивает размеры через getBoundingClientRect(), что и приводит к принудительным перекомпоновкам. Сам скрипт подключается автоматически.

Интересно то, что отладка велась в Chrome версии 151, а поддержка sizes="auto" реализована ещё в версии 126. То есть полифил выполняет работу в браузере, где функция поддерживается. Обычно в таких случаях полифил не отрабатывает.

Этот полифил не исключение. На страницу встроен скрипт, в котором есть функция проверки перед загрузкой скрипта. Также в самом скрипте функция дублируется. То есть проверка выполняется даже дважды. Вот код этой функции:

const polyfillAutoSizes = () => {
if (!window.PerformanceObserver?.supportedEntryTypes?.includes("paint")) {
return false
}
const userAgent = navigator.userAgent;
const platform = navigator.platform;
const maxTouchPoints = navigator.maxTouchPoints
|| 0;
const isIOS = /iPad|iPhone|iPod/.test(platform)
|| platform === "MacIntel"
&& maxTouchPoints > 1;
const isMacSafari = platform.indexOf("Mac") === 0
&& /Safari/.test(userAgent)
&& !/Chrome|Chromium|CriOS|FxiOS|Edg|OPR|Android/.test(userAgent);
const isAppleSafari = isIOS || isMacSafari;
const browserMajorVersion = pattern => {
const match = userAgent.match(pattern);
return match
? parseInt(match[1], 10)
: null
}

const chromeVersion = browserMajorVersion(
/Chrome\/(\d+)/
);
if (chromeVersion !== null) {
return chromeVersion < 126
}
const safariVersion = isAppleSafari
? browserMajorVersion(
/Version\/(\d+).*Safari\//
)
: null;
if (safariVersion !== null) {
return safariVersion < 27
}
const firefoxVersion = browserMajorVersion(
/Firefox\/(\d+)/
);
if (firefoxVersion !== null) {
return firefoxVersion < 150
}
return true
}
;
if (!polyfillAutoSizes()) {
return
}
В нём проверяется версия Chrome, Safari и Firefox на основе строки User Agent. Скрипт не должен подключаться, если версия Chrome меньше 126. Но он подключается и работает в Chrome 151. Где-то функция дала сбой и вернула true.

Выяснилось, что в моей строке User Agent не было ни Chrome, ни Safari, ни Firefox. Логика прошла все проверки, нигде не случился early return и в итоге дошло до return true. Вот такая у меня строка User Agent, где ни Chrome, ни его версии:

Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1
В теории User Agent выглядит как источник данных о браузере и версии. Может показаться вполне разумным брать оттуда информацию для активации полифила. Ведь известны браузеры и версии, где функция поддерживается.

На деле же строка User Agent максимально непредсказуемая, браузеры постоянно её меняют и могут врать, что и произошло в моём случае. В строке User Agent в Chrome не было ни намёка на Chrome. Браузеры делают это специально.

При разработке полифилов надёжнее будет полагаться на обнаружение функций (feature detection) — проверку фактического наличия и работоспособности функции в браузере. Это и должно служить основанием для подключения полифила.

Авторы полифила это уже поняли, поэтому открыт PR c заменой User Agent sniffing на обнаружение функций. Создаётся реальное изображение, ему задаются CSS-свойства, устанавливается атрибут и производится проверка вычисляемого значения.

const polyfillAutoSizes = () => {
// Avoid polyfilling if the browser is too old and doesn't support performance observer and paint timing
if (
!('PerformanceObserver' in window)
|| !PerformanceObserver
.supportedEntryTypes
.includes('paint')
) {
return false;
}

// Check UA styles for expected styles to support sizes auto.
const img = document.createElement('img');
img.style.setProperty(
'display',
'none',
'important'
);
img.style.setProperty(
'contain',
'revert',
'important'
);
img.sizes = 'auto';
document.documentElement.append(img);
const {contain} = getComputedStyle(img);
img.remove();
return contain !== 'size';
};

if (!polyfillAutoSizes()) {
return;
}
Этот способ намного надёжнее, хотя не всегда возможен. Есть функции, поддержку которых нельзя или сложно проверить таким образом. Тем не менее, во многих случаях это гораздо лучше проверки на основе User Agent.

#js #performance

694 0 3 14
Каталог
Каталог каналов и чатов Подборки каналов Поиск каналов Добавить канал/чат
Рейтинги
Рейтинг каналов Telegram Рейтинг чатов Telegram Рейтинг публикаций Рейтинги брендов и персон
API
API статистики API поиска публикаций API Callback
Наши каналы
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Почитать
Академия TGStat Исследование Telegram 2019 Исследование Telegram 2021 Исследование Telegram 2023
Контакты
Справочный центр Поддержка Почта Вакансии
Всякая всячина
Пользовательское соглашение Политика конфиденциальности Публичная оферта
Наши боты
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot