Ana içeriğe geç

TBT (Total Blocking Time) Nedir? JavaScript Engelleme Süresi

TBT Total Blocking Time Nedir - Teknik SEO Rehberi

TBT (Total Blocking Time), FCP'nin tamamlanmasından TTI'ya (Time to Interactive) uzanan pencerede tarayıcı ana thread'ini 50 milisaniyeden uzun süre tutan her JavaScript görevinin "engelleme katkısını" toplar. Sonuç milisaniye cinsinden tek bir sayıdır ve Lighthouse performans skorunun %30'unu oluşturur.

Pek çok geliştirici TBT ile FID ya da günümüzdeki karşılığı INP'yi (Interaction to Next Paint) eş anlamlı kullanıyor. İkisi farklı şeyler ölçüyor. TBT, laboratuvar ortamında üretilen sentetik bir metriktir; gerçek kullanıcı verisi içermez. FID ve INP ise saha metrikleridir: gerçek tıklamalar, gerçek yanıt süreleri. TBT yüksekse FID/INP de yüksek çıkma ihtimali artar, ama birebir denklem değildir; sayfa yükleme anında kimse tıklamıyorsa yüksek TBT düşük FID ile birlikte görülebilir. Bu ayrım pratikte önemlidir çünkü Lighthouse puanı TBT'ye göre hesaplanır, Google'ın sıralama sinyali olarak kullandığı Core Web Vitals ölçümü ise sahadan gelir.

50 milisaniyenin arkasındaki mantık

Tarayıcının ana iş parçacığı tek seferlik blok olarak çalışır: JavaScript yürütülürken kullanıcı girdisi kuyruğa girer, görev bitince işlenir. Bir görev 50ms'yi aşmadığı sürece bu gecikme fark edilmez; insan algısının pratik eşiği bu aralıktadır. 50ms üzerindeki her görev "uzun görev" (Long Task) sayılır.

Formül buradan netleşir: 80ms süren bir görev 30ms engelleme katkısı yapar (80 - 50 = 30). 120ms'lik bir görev 70ms katkı sağlar. FCP ile TTI arasındaki penceredeki tüm bu katkılar toplanır; bütünü TBT'dir. Lighthouse'da üç renk bandı var: yeşil 200ms altı, turuncu 200-600ms arası, kırmızı 600ms üstü. Ama bu eşikler mutlak değil; 300ms TBT, diğer metrikler iyi olsa bile toplam puanı ciddi çekiyor.

TTI de bu resmin parçasıdır. Sayfanın "gerçekten kullanılabilir" olduğu anı işaret eden TTI uzun görevlere bağlıdır; TBT yükseldikçe TTI de gecikir ve bileşik bir maliyet oluşturur; bazı sayfaların görsel olarak yüklenip de kullanıcı girdisine yanıt vermediği, yani "yüklendi ama kullanılamaz" göründüğü durum tam bu mekanizmadan kaynaklanır.

Yüksek TBT'nin gerçek kaynakları

Checkout sayfasında bir buton tıklaması 400ms geç yanıt veriyorsa sorun ağ değildir. Ana thread'i kilitleyen bir uzun görevdir. Bu uzun görevlerin kaynakları kısadır; ama her birinin çözümü farklıdır.

Senkron üçüncü taraf scriptleri ilk sıradadır. <head> içinde yüklenen ve async/defer almayan bir analitik kodu ya da reklam scripti HTML parse'ı duraklatır, ana thread'i tamamen ele geçirir. 200ms'lik bir script, sayfa boyunca 150ms TBT katkısı üretebilir.

İkinci kaynak fazla büyük JavaScript bundle'larıdır. Tek bir 300KB+ gzip JS dosyası yüklendiğinde tarayıcının onu parse ve compile etmesi uzun görev üretir. Bu ağdan değil, CPU'dan kaynaklanan bir sorundur; yavaş ağı simüle etmek bu durumu gizler çünkü download uzar, parse uzamaz; ikisi ayrı zaman çizelgelerinde görünür.

Gereksiz polyfill yığması üçüncüdür. Eski tarayıcıları hedefleme gerekçesiyle yüklenen polyfiller, modern tarayıcılarda sıfır işlev yapar ama parse maliyeti doğurur. Chrome 117 sonrasında Array.prototype.flat, Promise.allSettled, Object.fromEntries gibi yöntemler için polyfill yüklemenin anlamı yoktur; yine de yüzlerce projede bu yük taşınıyor.

Dördüncüsü büyük DOM boyutudur. 1500+ düğümlü bir DOM'da JavaScript'in her layout değişikliği reflow tetikler ve bu uzun görev olarak yakalanabilir. TBT raporunda görünen her görev doğrudan JS execution değildir; layout thrashing da buraya yansır.

Beşincisi çerçeve (framework) hydration maliyetidir; özellikle server-side rendering kullanan React ve Vue projelerinde sık karşılaşılır. Sunucuda render edilen HTML tarayıcıya gelir, görsel olarak hızlı görünür; ama ardından JavaScript yüklenir ve framework tüm bileşen ağacını "canlandırır" (hydrate eder). Bu süreç tek bir uzun görev olarak ana thread'i kilitleyebilir. 200KB üzerinde JS bundle'ı olan bir SSR sayfasında hydration tek başına 300-400ms TBT üretebilir. Lazy hydration veya partial hydration (Astro, Qwik gibi araçların sunduğu yaklaşımlar) bu maliyeti bölümlere ayırır.

Altıncı kaynak event listener yığılmasıdır. Scroll, resize veya mousemove gibi yüksek frekanslı olaylara bağlanan ve debounce/throttle almayan handler'lar, her tetiklemede ana thread'e yük bindirir. Tek bir handler hafif görünse de 50ms'lik bir scroll döngüsünde onlarca tetikleme TBT ölçümüne yansımayabilir; ancak DevTools Timeline'da bu pattern'i görmek mümkündür ve gerçek kullanıcı deneyimini doğrudan etkiler.

Müdahale yöntemleri ve trade-off'ları

Code splitting ilk tercih gibi görünür, ve çoğu zaman öyledir. Webpack veya Vite üzerinde import() ile dinamik split yapıldığında ilk yükleme paketinin boyutu düşer, parse maliyeti azalır, TBT iyileşir. Ancak bir trade-off var: sonraki etkileşimde ihtiyaç duyulan chunk henüz yüklenmemişse, tıklama anında ek bir ağ isteği tetiklenir; bu INP'yi etkiler.

defer ile async farkı sıklıkla karıştırılır. async scripti ağdan indirir ve hemen yürütür (indirme tamamlandığı anda, sıraya bakmadan). defer ise HTML parse bittikten sonra, dosya sırasına göre yürütür. Üçüncü taraf scriptleri için async uygundur; kendi kodunuz DOM'a bağımlıysa defer zorunludur. İkisini yanlış kullanmak TBT'yi düşürmez, zaman zaman yükseltir.

Web Worker yolu ağır hesaplama gerektiren durumlar için geçerlidir: görüntü işleme, şifreleme, büyük JSON parse işlemleri Worker'a taşındığında uzun görev kaybolur. Sınır açıktır; DOM erişimi Worker'dan yapılamaz, bu etrafında tasarım yapılması gerekir. Scheduler API'si ise (Chrome 94+) scheduler.postTask() ile görevlere öncelik atamanıza olanak tanır; doğrudan TBT sayısını değiştirmez, ama kullanıcı girdisinin kritik görevlerden önce işlenmesini sağlayarak INP'yi iyileştirir.

Önce ölçmeden optimize etmeyin. DevTools Performance sekmesinde uzun görevler kırmızı köşegen işaretle gösterilir; task duration ve call stack, Lighthouse'un verdiği genel dosya adından çok daha spesifik bilgi sağlar; özellikle aynı dosyadan birden fazla fonksiyonun farklı uzun görevler oluşturduğu durumlarda, bu ayrım olmadan hangi yola girilirse girilsin optimizasyon rastgele bir deneme olmaktan öteye geçemez.

requestIdleCallback kullanımı da TBT'yi dolaylı etkiler. Kritik olmayan işlemleri (analytics ping, görünmez içerik ön yükleme, önbellek temizleme) ana yüklemeden koparıp boşta kalan anlara ertelemek, FCP-TTI penceresindeki uzun görev yoğunluğunu azaltır. Tarayıcı bu callback'i her zaman çağırmayabilir; timeout parametresi eklenmezse uzun süre ertelenebilir. Bu nedenle kritik iş akışlarını requestIdleCallback'e taşımak değil, yalnızca gerçekten ertelenebilir işlemler için kullanmak doğru yaklaşımdır.

Lighthouse TBT değerini nasıl okumalısınız

Lighthouse'un "Reduce JavaScript execution time" ve "Avoid long main-thread tasks" uyarıları TBT'nin en doğrudan göstergesidir. Her uzun görev tablo olarak listelenir: süre, dosya adı ve kaynak frame. Üçüncü taraf scriptlerini buradan tespit etmek 10 saniye sürer.

Önemli bir detay: Lighthouse farklı çalıştırmalarda farklı TBT değeri üretebilir. Mobil simülasyon modunda CPU throttling 4x olarak uygulanır; aynı sayfa masaüstünde 80ms TBT gösterirken mobilde 420ms gösterebilir. Bu bir ölçüm hatası değil, yavaş cihazlarda gerçek davranışın yansımasıdır. Sitenize gelen trafiğin büyük bölümü düşük güçlü Android cihazlardan geliyorsa Lighthouse'u güçlü bir masaüstünde çalıştırıp "300ms olsa iyi" demek yanıltıcıdır.

Bir sayfanın TBT değerini hızlıca görmek istiyorsanız URL girerek hem TBT hem diğer performans metriklerini doğrudan görüntüleyebilirsiniz. Chrome DevTools Performance sekmesiyle birlikte kullanıldığında, hangi görevin kaç ms engelleme yaptığını kaynak dosya düzeyinde belirlemek mümkün hale gelir.

Hangi sayfalar için öncelik verilmeli

TBT her sayfada eşit öneme sahip değildir. Bir blog gönderisi, kullanıcının genellikle yüklenmesini bekleyip okumaya başladığı bir sayfadır; 350ms TBT çoğu durumda görünür hasar bırakmaz. Checkout adımı, giriş formu içeren bir sayfa veya arama sonuç sayfası başka bir tablo oluşturur; kullanıcı sayfa yüklenir yüklenmez etkileşime geçmek ister, TBT bu anlarda doğrudan hissedilir.

Optimizasyona nereden başlayacağınızı belirlerken şu soruyu sorun: hangi sayfada kullanıcının yüklenme anındaki etkileşimi en kritik? Cevap önceliği verir. Her sayfayı aynı anda optimize etmeye çalışmak, en yüksek getiri sağlayan noktaya yeterince odaklanmayı engeller.

Yüksek trafikli ama düşük etkileşimli sayfalarda (örneğin kategori listeleri veya makale arşivleri) TBT değerini 600ms'nin altına çekmek yeterli olabilir. Buna karşılık, dönüşüm odaklı bir ürün sayfasında ya da A/B testi çalışan bir açılış sayfasında aynı eşik kabul edilemez; 150ms altı hedeflemek gerçekçidir. Önceliklendirme bu bağlamsal kararlar üzerine kurulmalı, metrik değerine bakıp sayfalar arasında sıralama yapılmamalıdır.

LCP optimizasyonunda olduğu gibi, TBT için de aynı prensip geçerlidir: tek bir metriği izole etmek yanıltabilir. Core Web Vitals bütününe bakıldığında metrikler birbirini tetikler; TBT'yi düşürmeye çalışırken agresif code splitting INP'yi olumsuz etkileyebilir, büyük DOM boyutunu küçültmek hem TBT hem CLS puanını birlikte iyileştirebilir.

TBT sayısını takip etmek kolaydır. Onu doğru okumak (hangi görev, hangi dosyadan, hangi sayfa türünde, hangi cihaz grubunda sorun yaratıyor) çok daha değerlidir. Aynı 400ms TBT değeri bir sayfada sorunsuz geçerken başka bir sayfada ciddi dönüşüm kaybına yol açabilir; bağlam sayıyı her zaman geride bırakır. Metrik tek başına bir karar değil, bir soruşturma başlangıcıdır.