Your network connection is slow. Выявляем проблемы с сетью у пользователя

Павел Дадыкин, ROGII

Your network connection is slow.
Выявляем проблемы с сетью у пользователя

Павел Дадыкин
ROGII

Обо мне

Павел Дадыкин

📍 Живу в Ульяновске
💻 10 лет во фронтенде​
🔥 6 лет в компании ROGII
🧑‍💻 Выступал на FrontendConf 2024

 

Работа из офиса

Работа в поле

О чём доклад

Зачем пользователям знать о проблемах с их соединением?

Главная цель — улучшить UX

Пользователь не всегда понимает, что происходит

Он может ждать live-данные, а они не придут

Уведомление повышает доверие к продукту

Типы проблем

Браузер должен уметь всё это определять

Определяем Online/Offline

        
if (navigator.onLine) {
  console.log("online");
} else {
  console.log("offline");
}
        
      

События navigator.onLine

        
window.addEventListener('online', () => {/* сеть есть */});
window.addEventListener('offline', () => {/* сети нет */});
        
      

Особенности navigator.onLine

navigator.onLine === true

Что же показывает navigator.onLine?

 

Как же определить скорость соединения?

Network Information API

Браузерное API для получения информации о типе подключения и скорости

Свойства navigator.connection

Свойство Описание
effectiveType Тип соединения: 'slow-2g', '2g', '3g', '4g'
type Тип сети: 'wifi', 'cellular', 'bluetooth', 'ethernet', ...
downlink Оценка скорости в Mbps
downlinkMax Максимально возможная скорость
rtt Round-trip time (RTT) в миллисекундах
saveData Включён ли режим экономии трафика

Network Information API

Resource Timing API

Resource Timing API

Пример Performance Resource Timing

Типичные метрики Resource Timing

Метрика Формула Назначение
TCP Handshake connectEnd - connectStart Время установления TCP-соединения
DNS Lookup domainLookupEnd - domainLookupStart Время разрешения DNS-имени
Redirect Time redirectEnd - redirectStart Задержка из-за переадресации
Request Time responseStart - requestStart Время между отправкой запроса и первым байтом ответа
TLS Negotiation requestStart - secureConnectionStart Время на установление HTTPS-соединения
Fetch Time responseEnd - fetchStart Общее время загрузки (без редиректов)

Типичные метрики Resource Timing

Метрика Формула Назначение
Service Worker fetchStart - workerStart Задержка на обработку запроса в Service Worker
Контент сжат? decodedBodySize ≠ encodedBodySize Проверка наличия сжатия (например, gzip)
Использован кэш? transferSize === 0 Загрузка из локального кэша
Современный протокол? nextHopProtocol Ожидается: h2 или h3 (HTTP/2/3)
Блокирует отрисовку? renderBlockingStatus Указывает, задерживает ли ресурс first paint

Timing в Developer Tools

📊 Сбор пользовательских метрик

Определяем медленное соединение

  1. Следим за fetch-запросами
  2. Смотрим время загрузки (duration)
  3. Смотрим объём (transferSize, в байтах)
  4. Вычисляем скорость (transferSize / duration)
  5. Если скорость ниже какого-то порога – сигнализируем

Получение информации о запросах

        
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(`${entry.name}, ${entry.startTime}`);
  }
});

observer.observe({ type: 'resource' });
        
      

Типы записей PerformanceObserver

Тип (entryType) Описание
resource Загрузка ресурсов: img, fetch, script и др.
navigation Полные данные о загрузке страницы (TTFB, redirect, DOM и др.)
mark Пользовательские метки времени (performance.mark())
measure Промежутки между метками (performance.measure())
paint Ранние метрики отрисовки: first-paint, first-contentful-paint
first-input Время отклика на первое взаимодействие (FID)
event Задержки обработки событий (например, click, input)

Вычисление скорости ресурса

        
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];

if (lastEntry.initiatorType === 'fetch') {
  const size = lastEntry.transferSize;
  const duration = lastEntry.duration;
  const speed = size / duration;
  const isConnectionFast = speed > SLOW_CONNECTION_SPEED;
}
        
      

Фильтрация записей

Что же считать «низкой» скоростью?

Оценка скорости соединения

Скорость Примерная оценка
< 50 КБ/сек. Очень медленно (пользователь заметит лаги)
50–150 КБ/сек. Замедленное поведение, страдает UX
150–500 КБ/сек. Приемлемо для большинства действий
> 500 КБ/сек. Хорошее подключение

WebSockets

WebSockets

Метрики WebSockets можно отслеживать вручную:
        
const wsStart = performance.now();
const socket = new WebSocket('wss://...');
socket.addEventListener('open', () => {
  const wsConnected = performance.now();
  console.log(`Connected in ${wsConnected - wsStart}ms`);
});
        
      

Что может пойти не так?

Проблемы с CORS

Проблемы с CORS

Проблемы с CORS

Решение 2. 🛠️ Не учитываем запросы на другой домен
            
if (entry.name.startsWith(window.location.origin)) {
  // считаем скорость
}
            
          

Как наш сервис должен реагировать?

Показываем уведомление

Отображение уведомлений

Блокируем интерактивные действия, которые могут привести к ошибке

Подгружаем облегчённую версию контента

Начинаем повторную отправку запросов

Как же это всё проверить?

Эмуляция в браузере

Выводы

Пожалуйста, оставьте свой отзыв

Павел Дадыкин
ROGII

https://meloman4eg.github.io/network-issues-fc-2025/

@meloman4eg