Merhaba arkadaşlar, bugün başımı çok ağrıtan bir performans sorununu ve onu nasıl bir CSS özelliğiyle adeta sihir gibi çözdüğümü anlatacağım. Uzun, içerik yüklü blog sayfalarımız vardı. Kullanıcılar sayfayı açtığında, ekranda görünmeyen yüzlerce yorum ve resmin de yüklenmesini bekliyordu. Bu da Largest Contentful Paint (LCP) ve Cumulative Layout Shift (CLS) gibi Core Web Vitals metriklerimizi mahvediyordu. İlk Lighthouse raporunu gördüğümde kafayı yemiştim diyebilirim.
Sorunun Tam Olarak Neydi?
Sayfamızda, ana makalenin hemen altında, kullanıcı yorumları bölümü vardı. Bazen 200-300 yorum oluyordu ve her yorumda avatar, kullanıcı adı, tarih ve metin vardı. Tarayıcı, sayfa yüklendiği anda, kullanıcının henüz görmediği ve belki de asla kaydırıp görmeyeceği bu 300 yorumun tamamını layout'ını hesaplıyor, stil uyguluyor ve boyuyordu (render ediyordu). Bu, ana iş parçacığını (main thread) kilitleyen, korkunç bir iş yüküydü. Kullanıcı, makalenin ilk paragrafını okumaya başlamak için bile fazladan 2-3 saniye beklemek zorunda kalıyordu.
Çözüm: content-visibility: auto ile Tanışmam
Performans optimizasyonu araştırırken, CSS Containment özelliklerinden biri olan `content-visibility` ile karşılaştım. Bu özellik, bir elementin render ağacından (rendering tree) çıkarılmasını ve sadece ihtiyaç duyulduğunda (yani görünür alana yaklaştığında) render edilmesini sağlıyor. İşte benim kullandığım en temiz çözüm buydu.
Temel mantık şu: "Ekranda görünmeyen bölümleri, görünmeye başlayana kadar render etme, layout hesaplama."
Nasıl Uyguladım? (Kod Zamanı!)
İlk adım, render maliyeti yüksek olan bölümleri belirlemek oldu. Bizim için bu, yorumlar listesiydi. Yapımız şuna benziyordu:
`user-comments` bölümüne ve her bir `comment` div'ine `content-visibility: auto` uyguladım. Ancak dikkat edilmesi gereken çok önemli bir nokta var: Bu özelliği kullanırken, elementin boyutunu (height) tahmin etmek için `contain-intrinsic-size` değerini de vermeliyiz. Aksi takdirde, elementler görünür alana girene kadar boyutsuz (0px yükseklikte) kabul edilir ve sayfa kaydırma çubuğu (scrollbar) sürekli zıplar, bu da berbat bir kullanıcı deneyimine yol açar.
İşte uyguladığım CSS:
Sonuçlar ve Lighthouse Karşılaştırması
Değişiklikten sonraki ilk Lighthouse audit'inde gözlerime inanamadım. LCP süresi %65, Toplam Engelleme Süresi (Total Blocking Time) ise %70'ten fazla iyileşmişti. Sayfanın ilk render süresi neredeyse 10 kata yakın hızlandı. En güzeli, kullanıcı deneyiminde hiçbir bozulma olmamasıydı. Kullanıcı yorumlar bölümüne doğru kaydırdıkça, yorumlar sorunsuz bir şekilde görünür hale geliyor ve render ediliyordu. Sanki "lazy loading" mantığı, ama sadece resimler için değil, tüm DOM elementleri için!
Dikkat Edilmesi Gerekenler
1. contain-intrinsic-size değerini doğru tahmin etmek çok önemli. Çok küçük verirseniz, kaydırma sırasında layout shift (yer değiştirme) yaşanır. Çok büyük verirseniz, faydası azalır. Mümkünse sabit yükseklikli bileşenlerde kullanmak en iyisi.
2. Bu özelliği, sayfadaki HER elemente uygulamayın. Sadece render maliyeti yüksek, görece bağımsız ve ekranda görünmeyen büyük bloklar için kullanın.
3. Tarayıcı desteği oldukça iyi (Chromium ve Edge'de tam destek, Firefox'ta deneysel destek). Desteklemeyen tarayıcılar için bu kuralı yok sayacaklarından geriye dönük uyumluluk sorunu yok.
Sonuç olarak, `content-visibility: auto` benim için bir kurtarıcı oldu. Özellikle admin panelleri, haber listeleri, uzun dokümantasyon sayfaları gibi içerik yoğun projelerde performansı inanılmaz derecede artırıyor.
Siz daha önce `content-visibility` veya `CSS Containment` özelliklerini kullandınız mı? Uzun listelerde performansı artırmak için sizin tercih ettiğiniz farklı bir yöntem (React Virtualization, Intersection Observer vb.) var mı? Yorumlarda deneyimlerinizi paylaşın, tartışalım!
Sayfamızda, ana makalenin hemen altında, kullanıcı yorumları bölümü vardı. Bazen 200-300 yorum oluyordu ve her yorumda avatar, kullanıcı adı, tarih ve metin vardı. Tarayıcı, sayfa yüklendiği anda, kullanıcının henüz görmediği ve belki de asla kaydırıp görmeyeceği bu 300 yorumun tamamını layout'ını hesaplıyor, stil uyguluyor ve boyuyordu (render ediyordu). Bu, ana iş parçacığını (main thread) kilitleyen, korkunç bir iş yüküydü. Kullanıcı, makalenin ilk paragrafını okumaya başlamak için bile fazladan 2-3 saniye beklemek zorunda kalıyordu.
Performans optimizasyonu araştırırken, CSS Containment özelliklerinden biri olan `content-visibility` ile karşılaştım. Bu özellik, bir elementin render ağacından (rendering tree) çıkarılmasını ve sadece ihtiyaç duyulduğunda (yani görünür alana yaklaştığında) render edilmesini sağlıyor. İşte benim kullandığım en temiz çözüm buydu.
Temel mantık şu: "Ekranda görünmeyen bölümleri, görünmeye başlayana kadar render etme, layout hesaplama."
İlk adım, render maliyeti yüksek olan bölümleri belirlemek oldu. Bizim için bu, yorumlar listesiydi. Yapımız şuna benziyordu:
HTML:
<article class="blog-post">... Makale içeriği ...</article>
<section class="user-comments">
<div class="comment">...1. yorum...</div>
<div class="comment">...2. yorum...</div>
<!-- ... 298 yorum daha ... -->
</section>
`user-comments` bölümüne ve her bir `comment` div'ine `content-visibility: auto` uyguladım. Ancak dikkat edilmesi gereken çok önemli bir nokta var: Bu özelliği kullanırken, elementin boyutunu (height) tahmin etmek için `contain-intrinsic-size` değerini de vermeliyiz. Aksi takdirde, elementler görünür alana girene kadar boyutsuz (0px yükseklikte) kabul edilir ve sayfa kaydırma çubuğu (scrollbar) sürekli zıplar, bu da berbat bir kullanıcı deneyimine yol açar.
İşte uyguladığım CSS:
CSS:
.user-comments {
content-visibility: auto;
/ Ortalama bir yorum yüksekliği + margin/padding. Bu değeri kendi içeriğinize göre ayarlayın. /
contain-intrinsic-size: auto 600px;
}
.comment {
content-visibility: auto;
/ Her bir yorum kartı için ortalama yükseklik /
contain-intrinsic-size: auto 120px;
margin-bottom: 1rem;
}
Değişiklikten sonraki ilk Lighthouse audit'inde gözlerime inanamadım. LCP süresi %65, Toplam Engelleme Süresi (Total Blocking Time) ise %70'ten fazla iyileşmişti. Sayfanın ilk render süresi neredeyse 10 kata yakın hızlandı. En güzeli, kullanıcı deneyiminde hiçbir bozulma olmamasıydı. Kullanıcı yorumlar bölümüne doğru kaydırdıkça, yorumlar sorunsuz bir şekilde görünür hale geliyor ve render ediliyordu. Sanki "lazy loading" mantığı, ama sadece resimler için değil, tüm DOM elementleri için!
1. contain-intrinsic-size değerini doğru tahmin etmek çok önemli. Çok küçük verirseniz, kaydırma sırasında layout shift (yer değiştirme) yaşanır. Çok büyük verirseniz, faydası azalır. Mümkünse sabit yükseklikli bileşenlerde kullanmak en iyisi.
2. Bu özelliği, sayfadaki HER elemente uygulamayın. Sadece render maliyeti yüksek, görece bağımsız ve ekranda görünmeyen büyük bloklar için kullanın.
3. Tarayıcı desteği oldukça iyi (Chromium ve Edge'de tam destek, Firefox'ta deneysel destek). Desteklemeyen tarayıcılar için bu kuralı yok sayacaklarından geriye dönük uyumluluk sorunu yok.
Sonuç olarak, `content-visibility: auto` benim için bir kurtarıcı oldu. Özellikle admin panelleri, haber listeleri, uzun dokümantasyon sayfaları gibi içerik yoğun projelerde performansı inanılmaz derecede artırıyor.
Siz daha önce `content-visibility` veya `CSS Containment` özelliklerini kullandınız mı? Uzun listelerde performansı artırmak için sizin tercih ettiğiniz farklı bir yöntem (React Virtualization, Intersection Observer vb.) var mı? Yorumlarda deneyimlerinizi paylaşın, tartışalım!