Оптимизация Total Blocking Time (TBT) сайта

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Оптимизация Total Blocking Time (TBT) сайта
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Оптимизация Total Blocking Time (TBT) сайта

Total Blocking Time — сумма «заблокированных» миллисекунд между FCP и TTI. Задача длиннее 50 мс в main thread считается Long Task; всё, что сверх этих 50 мс, суммируется в TBT. Если у вас три задачи по 80 мс — TBT составит (80-50)×3 = 90 мс. Это Core Web Vital-смежная метрика: TBT в лабораторных условиях коррелирует с INP в реальных.

Хорошее значение TBT по Lighthouse: менее 200 мс (десктоп), менее 300 мс (мобильный симулятор с 4× CPU throttling).

Диагностика: находим Long Tasks

Первый шаг — найти, что именно создаёт Long Tasks. Chrome DevTools, вкладка Performance: записываем загрузку страницы, смотрим красные полосы над main thread.

Программно через Long Task API:

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

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

attribution покажет, в каком фрейме или скрипте возникла задача. Отправляем данные в аналитику для field monitoring:

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

// В обработчике
fetch('/api/longtasks', {
  method: 'POST',
  body: JSON.stringify({
    tasks: list.getEntries().map(e => ({
      duration: e.duration,
      script: e.attribution[0]?.name || 'unknown',
      url: location.href,
    })),
  }),
  keepalive: true,
});

Причина №1: тяжёлый JavaScript при старте

Самая частая причина высокого TBT — большой JS-бандл, который парсируется и выполняется синхронно при загрузке. Каждый мегабайт JS требует ~1 секунды парсинга на среднем мобильном устройстве.

Решение: code splitting + lazy loading. Пример для React:

// До: всё грузится сразу
import HeavyChart from './HeavyChart';
import DataTable from './DataTable';

// После: грузим только при необходимости
const HeavyChart = lazy(() => import('./HeavyChart'));
const DataTable = lazy(() => import('./DataTable'));

function Dashboard() {
  const [showChart, setShowChart] = useState(false);

  return (
    <div>
      <button onClick={() => setShowChart(true)}>Показать график</button>
      {showChart && (
        <Suspense fallback={<Skeleton />}>
          <HeavyChart />
        </Suspense>
      )}
    </div>
  );
}

Для роутов — React.lazy + react-router:

const ProductPage = lazy(() => import('./pages/ProductPage'));
const CheckoutPage = lazy(() => import('./pages/CheckoutPage'));

// Все страницы загружаются только при переходе

Причина №2: синхронные сторонние скрипты

Google Tag Manager, чаты, пиксели рекламных сетей — каждый сторонний скрипт может создавать Long Tasks. Особенно если загружен синхронно.

Диагностика в Chrome DevTools: Performance → Bottom-up → группировка по домену. Видим, сколько времени main thread тратит на каждый сторонний скрипт.

Решения:

<!-- Вместо синхронного: -->
<script src="https://widget.example.com/chat.js"></script>

<!-- Используем async или defer: -->
<script src="https://widget.example.com/chat.js" async></script>

<!-- Или отложенная загрузка после взаимодействия: -->
<script>
  function loadChat() {
    const s = document.createElement('script');
    s.src = 'https://widget.example.com/chat.js';
    document.head.appendChild(s);
  }

  // Загружаем после первого взаимодействия пользователя
  ['click', 'scroll', 'keydown'].forEach(event => {
    window.addEventListener(event, loadChat, { once: true });
  });
</script>

Для GTM: отложенный запуск триггера «DOM Ready» вместо «Page View» для некритичных тегов.

Причина №3: тяжёлые вычисления в main thread

Сортировка больших массивов, сложные DOM-манипуляции, синхронные XHR — всё это блокирует main thread.

Web Workers для вычислений:

// worker.js
self.onmessage = function(e) {
  const { data, operation } = e.data;

  let result;
  switch (operation) {
    case 'sort':
      result = heavySort(data);
      break;
    case 'filter':
      result = complexFilter(data);
      break;
  }

  self.postMessage(result);
};

// main.js
const worker = new Worker('/worker.js');

worker.postMessage({ data: largeArray, operation: 'sort' });
worker.onmessage = (e) => {
  setTableData(e.data);
};

Scheduler API (Chrome 94+) для разбивки задач:

async function processItems(items) {
  const CHUNK_SIZE = 50;

  for (let i = 0; i < items.length; i += CHUNK_SIZE) {
    const chunk = items.slice(i, i + CHUNK_SIZE);
    processChunk(chunk);

    // Отдаём управление браузеру между чанками
    if (i + CHUNK_SIZE < items.length) {
      await scheduler.yield();
    }
  }
}

Полифил для браузеров без scheduler.yield():

function yieldToMain() {
  return new Promise(resolve => setTimeout(resolve, 0));
}

Причина №4: тяжёлый hydration в SSR/SSG

Next.js, Nuxt, Gatsby — при hydration фреймворк повторно обходит весь DOM и привязывает обработчики событий. На больших страницах это одна большая Long Task.

Решения:

  • Partial hydration / Islands architecture — гидратируем только интерактивные компоненты
  • Progressive hydration — откладываем hydration для off-screen компонентов
  • React 18 startTransition — помечаем некритичные обновления как переходы
import { startTransition } from 'react';

function SearchResults({ query }) {
  const [results, setResults] = useState([]);

  function handleSearch(newQuery) {
    // Обновление результатов — не срочное
    startTransition(() => {
      setResults(fetchResults(newQuery));
    });
  }
  // ...
}

Измерение TBT в полевых условиях

TBT нельзя измерить из реального браузера напрямую, но INP (Interaction to Next Paint) — его production-аналог. Используем web-vitals:

npm install web-vitals
import { onINP, onFCP, onLCP } from 'web-vitals';

function sendToAnalytics(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    rating: metric.rating,  // 'good', 'needs-improvement', 'poor'
    id: metric.id,
    url: location.href,
  });

  navigator.sendBeacon('/api/vitals', body);
}

onINP(sendToAnalytics);
onFCP(sendToAnalytics);
onLCP(sendToAnalytics);

Контрольный чеклист оптимизации TBT

  1. Разбили JS-бандл: initial chunk < 150 kB gzip, роуты ленивые
  2. Сторонние скрипты: все некритичные — с defer или отложенной загрузкой
  3. Тяжёлые вычисления вынесены в Web Worker
  4. Длинные синхронные циклы разбиты через scheduler.yield()
  5. React: startTransition для некритичных обновлений, useDeferredValue для фильтрации
  6. Проверили рекламные сети: AdSense, Yandex Ads часто дают 200–500 мс TBT

Сроки

Диагностика и составление плана оптимизации — 1–2 рабочих дня. Code splitting + ленивая загрузка типового React-приложения — 3–5 рабочих дней. Полный цикл: диагностика, оптимизация, настройка мониторинга INP в продакшне — 1,5–3 недели в зависимости от сложности приложения.