Skip to content
26 / 36

Что такое Resource Timing API?


Полный ответ

Каждый загруженный ресурс создаёт запись PerformanceResourceTiming:

JavaScript
// Получить все загруженные ресурсы
const resources = performance.getEntriesByType('resource');

for (const res of resources) {
  console.log({
      name: res.name,                  // URL ресурса
      type: res.initiatorType,         // "script", "css", "img", "fetch", "xmlhttprequest"
      duration: res.duration,          // общее время загрузки (мс)
      transferSize: res.transferSize,  // размер по сети (0 = из кеша)
      protocol: res.nextHopProtocol,   // "h2", "h3", "http/1.1"
    });
}

Детальная разбивка тайминга ресурса:

JavaScript
function analyzeResource(entry) {

  // Если ресурс из кеша, сетевые тайминги будут 0
  const fromCache = entry.transferSize === 0;

  if (fromCache) {
    return { name: entry.name, source: 'cache', duration: entry.duration };
  }

  return {
    name: entry.name,
    source: 'network',
    dns: entry.domainLookupEnd - entry.domainLookupStart,
    tcp: entry.connectEnd - entry.connectStart,
    tls: entry.secureConnectionStart > 0
    ? entry.connectEnd - entry.secureConnectionStart
    : 0,
    ttfb: entry.responseStart - entry.requestStart,
    download: entry.responseEnd - entry.responseStart,
    total: entry.duration,
    size: entry.transferSize,
  };
}

const resources = performance.getEntriesByType('resource');
const analysis = resources.map(analyzeResource);
console.table(analysis);

Наблюдение через PerformanceObserver:

JavaScript
const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {

      // Предупреждение о медленных ресурсах
      if (entry.duration > 1000) {
        console.warn(
          `Медленный ресурс (${entry.duration.toFixed(0)} ms): ${entry.name}`
        );
      }

      // Предупреждение о больших ресурсах
      if (entry.transferSize > 500_000) {
        console.warn(
          `Тяжёлый ресурс (${(entry.transferSize / 1024).toFixed(0)} KB): ${entry.name}`
        );
      }
    }
  });

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

Ограничения для кросс-доменных ресурсов:

JavaScript
// Для ресурсов с другого домена тайминги по умолчанию обнулены
const cdnResource = resources.find((r) => r.name.includes('cdn.example.com'));

// Без Timing-Allow-Origin все сетевые тайминги будут 0:
console.log(cdnResource.requestStart);  // 0
console.log(cdnResource.responseStart); // 0
// duration и startTime доступны, но детали скрыты

// Сервер должен отправить заголовок:

// Timing-Allow-Origin: *
// или

// Timing-Allow-Origin: https:// your-site.com

📌 Реальные кейсы

Поиск узких мест загрузки:

JavaScript
function findBottlenecks() {
  const resources = performance.getEntriesByType('resource');

  // Группировка по типу
  const byType = new Map();
  for (const res of resources) {
    const type = res.initiatorType;
    if (!byType.has(type)) byType.set(type, []);
    byType.get(type).push(res);
  }

  for (const [type, entries] of byType) {
    const totalSize = entries.reduce((sum, e) => sum + e.transferSize, 0);
    const avgDuration = entries.reduce((sum, e) => sum + e.duration, 0) / entries.length;
    const fromCache = entries.filter((e) => e.transferSize === 0).length;

    console.log(`${type}: ${entries.length} ресурсов`);
    console.log(`  Общий размер: ${(totalSize / 1024).toFixed(0)} KB`);
    console.log(`  Среднее время: ${avgDuration.toFixed(0)} ms`);
    console.log(`  Из кеша: ${fromCache}/${entries.length}`);
  }

  // Топ-5 самых медленных
  const slowest = [...resources]
  .sort((a, b) => b.duration - a.duration)
  .slice(0, 5);

  console.log('
    Топ-5 медленных ресурсов:');
  for (const res of slowest) {
    console.log(`  ${res.duration.toFixed(0)} ms -- ${res.name.split('/').pop()}`);
  }
}

window.addEventListener('load', () => setTimeout(findBottlenecks, 100));

Лимит буфера ресурсов:

JavaScript
// По умолчанию браузер хранит 250 записей ресурсов

// Для страниц с большим количеством ресурсов буфер переполняется

// Увеличение буфера
performance.setResourceTimingBufferSize(500);

// Обработка переполнения
performance.addEventListener('resourcetimingbufferfull', () => {

    // Сохранить текущие записи
    const entries = performance.getEntriesByType('resource');
    sendToAnalytics(entries);

    // Очистить буфер
    performance.clearResourceTimings();
  });

Резюме

Resource Timing API дает тайминги каждого ресурса на странице. Ключевые поля: duration, transferSize, initiatorType. Для кросс-доменных ресурсов нужен заголовок Timing-Allow-Origin. Буфер по умолчанию -- 250 записей, при необходимости его нужно увеличить или периодически очищать.

Что такое Resource Timing API? | JScriptiser