Bir :has() seçicisi TBT’nin %70’iydi: 369 → 108 ms.
Ana sayfanın engelleme süresi bir turda altı kat arttı; suçlu yeni sahneler ya da animasyon kütüphanesi değil, o sayfada hiç eşleşmeyen on CSS kuralıydı.
Bir turun ardından ana sayfanın engelleme süresi 61 milisaniyeden 369 milisaniyeye çıkmıştı. İlk şüpheli her zamanki gibi JavaScript’ti: o turda iki yeni sahne eklenmişti ve animasyon kütüphanesi uzun görevler üretiyordu. Ölçüm başka bir şey söyledi — pahalı olan betik değil, betiğin dokunduğu belgeydi.
Hipotez
Tarayıcı izini betik, stil ve yerleşim diye ayırdığımızda yükün nerede olduğu görünür hâle geldi: stil hesabı toplamı 423 milisaniyeden 1.392 milisaniyeye çıkmıştı. Belge gerçekten büyümüştü (671 elemandan 1.733 elemana), ama asıl mesele büyüme değildi: her kare belgenin TAMAMINI yeniden hesaplatıyordu. Hipotez buydu — köke bağlı bir seçici var ve her küçük değişiklikte tüm ağacı geçersiz kılıyor.
Yöntem
İki ağaç yan yana koşuldu: dört gün önceki canlı yapı geçici bir çalışma kopyasında, güncel ağaç yanında, ikisi de aynı mobil profilde (412 × 823, piksel oranı 1,75, işlemci dört kat yavaşlatılmış). Sonra stil dosyası istek yakalayıcıyla uçuşta dönüştürüldü — diskte tek bayt değişmeden kural aileleri teker teker çıkarıldı ve her seferinde engelleme süresinin medyanı okundu. Tarayıcının kendi geçersizleme izi de açıldı; o iz hangi elemanın hangi seçiciden etkilendiğini satır satır yazıyor.
Tarih ve ortam
- tarih → 2026-09-21 · yerel A/B · iki ağaç yan yana
- profil → 412 × 823 · DPR 1,75 · CPU dört kat yavaş
- ölçü → TBT medyanı · stil hesabı toplamı · eleman sayısı
- iz → devtools.timeline.invalidationTracking
Sonuç: bisect
Kuralları aileler hâlinde çıkarmak suçluyu tek başına bıraktı. Tüm :has() seçicileri kaldırıldığında engelleme süresi 369 milisaniyeden 104 milisaniyeye iniyordu; yalnız öznesi html ya da body olanlar kaldırıldığında 115 milisaniyeye. Tek tek denendiğinde yükün neredeyse tamamı tek bir aileye toplandı.
- olduğu gibi → TBT 369 ms
- tüm :has() çıkarılmış → 104 ms
- yalnız html/body özneli → 115 ms
- :has(.menu … → 431 ms
- :has(main.oda) … → 430 ms
- :has(.sayfa__ … → 112 ms
Suçlu: hiç eşleşmeyen on kural
Kalan aday, temel stil dosyasının on kuralıydı ve hepsi aynı kalıptaydı: öznesi html olan bir :has(), ardından geniş bir sağ taraf. Bu kuralların ana sayfada eşleşmediğini de ölçtük — hiçbiri o sayfada bir şey boyamıyordu. Yine de her tween, her motor kurulumu belgenin tamamını geçersiz kılıyordu. Çünkü tarayıcı «bu değişiklik kökteki :has() sonucunu değiştirmiş olabilir mi» sorusuna ancak alt ağacı yeniden hesaplayarak cevap verebiliyor. Geçersizleme izi bunu adıyla söylüyordu: html ve body üzerinde «:has() tarafından etkilenen» 350 ve 347 kayıt, yanında «geçersizleme kümesi alt ağacı geçersiz kılar» notu.
Çözüm: soruyu derlemeye taşımak
Sorulan soru aynı kaldı, soran taraf değişti. Bir sayfanın hangi konağı taşıdığı yükleme sonrası değişmeyen bir yapıdır — yani derleme zamanında zaten bilinir. Üretici son HTML’e bakıyor ve kökün üstüne bir im basıyor, stil de artık o imi soruyor. Özgüllük birebir aynı kaldı, yani hiçbir kuralın sırası kaymadı; hesaplanan stil farkı on iki rotada, iki genişlikte, on beş binden fazla eleman üzerinde sıfır çıktı.
- yerel TBT → 369 → 97–108 ms (5 koşum)
- stil hesabı → 1.415 → 783 ms
- hesaplanan stil farkı → 0 (12 rota × 2 genişlik)
- canlı TBT medyanı → 293 → 83 / 86 ms
Nöbetçi
Bulgu bir kapıya bağlandı, çünkü yorumla yazılmış bir karar bir sonraki turda sessizce bozulur. Kapı üç şey soruyor: im ile konak diskte iki yönlü eşleşiyor mu, üretilmiş stil dosyasında o kalıp kaldı mı, ve iki rotanın yüklenmesinde kökün alt ağacı kaç kez geçersiz kılınıyor. Kapının düşebildiği, kusur geri konarak doğrulandı: eski kurallar geri yazıldığında sayaç ikiden kırk dokuza çıkıyor. Düşemeyen bir nöbetçi yeşil değildir, yalnız sessizdir.
Sınırlar
Buradaki sayılar bu makinenin ve bu belgenin sayıları; başka bir sayfada oran başka çıkar. Çıkarılacak kural «:has() yavaştır» da değil — o seçici yerinde kullanıldığında ucuzdur. Kural şu: öznesi html ya da body olan bir :has(), eşleşmediği sayfada bile her değişiklikte belgenin tamamını geçersiz kılar. Yükleme sonrası değişmeyen bir yapıyı çalışma zamanında seçiciyle sormak yerine derlemede işaretlemek gerekiyor.
Bir seçicinin maliyeti eşleştiği yerde değil, aradığı yerde.