Merhaba arkadaşlar, bugün performans optimizasyonu konusunda kafayı yediğim ve sonunda bulduğum çok temiz bir çözümü paylaşacağım. Özellikle Lighthouse veya PageSpeed Insights skorlarında "Eliminate render-blocking resources" uyarısını görüp de sinir krizi geçirenlerdenseniz, bu yazı tam size göre. Ben de aynı dertten mustariptim, ta ki above-the-fold CSS konseptini doğru uygulayana kadar.
Render Bloklama Nedir ve Neden Can Sıkar?
Geleneksel olarak, CSS dosyamızı head içinde
şeklinde bağladığımızda, tarayıcı şu işlemleri yapar: CSS dosyasını indir, ayrıştır (parse et) ve sonra DOM'u oluşturmaya/oluşturulmuş DOM'u boyamaya (render etmeye) devam et. İşte bu indirme ve ayrıştırma sürecinde, tarayıcı sayfanın render edilmesini tamamen durdurur. Dosya büyükse veya ağ yavaşsa, kullanıcı bomboş bir beyaz ekrana bakakalır. İşte buna render bloklama deniyor ve modern web'de kabul edilemez bir durum.
Peki çözüm? Kritik olan CSS'i (above-the-fold, yani ilk ekranda görünen her şeyin stilini) inline yapmak, geri kalanını ise asenkron olarak yüklemek.
Kritik CSS'i (Above-the-fold) Nasıl Belirledim?
İlk adım, "kritik CSS" dediğimiz şeyi çıkarmak. Bunun için birkaç yol var: Manuel olarak inceleyebilirsiniz, Chrome DevTools'un Coverage tab'ını kullanabilirsiniz veya benim tercihim olan otomasyon araçlarını.
Ben projemde critical npm paketini kullandım. Bu paket, belirlediğiniz viewport boyutlarına göre (örneğin masaüstü ve mobil) sayfanızı tarar ve sadece ilk ekranda görünen elementlerin CSS kurallarını çıkarır.
Kurulum ve temel kullanımı şöyle:
Bu komut çalıştırıldığında, `src/index.html` dosyasını analiz eder, kritik CSS'i bulur ve onu inline `<style>` etiketleri içine yazar, geri kalan CSS bağlantılarını da dönüştürerek yeni bir `dist/index-critical.html` dosyası oluşturur.
Uygulama: Inline CSS ve Async Yükleme
Critical aracının çıkardığı sonucu manuel olarak da uygulayabilirsiniz. İşte elde ettiğiniz HTML yapısı şuna benzeyecek:
Buradaki sihir şu: `<style>` etiketi içindeki CSS anında uygulanır, çünkü HTML ile birlikte gelir. `styles.defer.css` dosyası ise `rel="preload"` ve `onload` atribütleri sayesinde render işlemini hiç bloklamadan, sayfa yüklendikten sonra arka planda indirilir ve hazır olduğunda uygulanır. `noscript` etiketi de JavaScript kapalı kullanıcılar için bir fallback sağlar.
Elde Ettiğim Sonuçlar ve Dikkat Edilmesi Gerekenler
Bu tekniği uyguladıktan sonra, orta ölçekli bir projemdeki First Contentful Paint (FCP) süresi neredeyse %40 azaldı. Lighthouse skorum "Performance" alanında 85'lerden 95+ seviyesine fırladı.
Ancak dikkat etmeniz gereken noktalar var:
1. Inline CSS boyutunu küçük tutun. 15KB'ı geçmemeye çalışın. Çok büyük olursa, HTML'nin ilk baytını da geciktirirsiniz.
2. Kritik CSS, her sayfa için farklı olabilir. Ana sayfa, ürün sayfası, blog yazısı... Hepsi için ayrı kritik CSS çıkarmak en iyi sonucu verir.
3. Build sürecinize bu işlemi entegre edin (Webpack, Gulp, vs. pluginleri var). Manuel yapmak sürdürülebilir değil.
Siz de bu yöntemi denediniz mi? Özellikle Next.js, Gatsby gibi framework'lerde veya WordPress temalarında kritik CSS çıkarma konusunda farklı araçlar/plugin'ler mi kullanıyorsunuz? Ya da "Bununla uğraşmaya değmez, CDN ve caching ile hallolur" diyenlerdenseniz, fikirlerinizi merak ediyorum. Yorumlarda buluşalım!
Geleneksel olarak, CSS dosyamızı head içinde
HTML:
<link rel="stylesheet" href="style.css">
Peki çözüm? Kritik olan CSS'i (above-the-fold, yani ilk ekranda görünen her şeyin stilini) inline yapmak, geri kalanını ise asenkron olarak yüklemek.
İlk adım, "kritik CSS" dediğimiz şeyi çıkarmak. Bunun için birkaç yol var: Manuel olarak inceleyebilirsiniz, Chrome DevTools'un Coverage tab'ını kullanabilirsiniz veya benim tercihim olan otomasyon araçlarını.
Ben projemde critical npm paketini kullandım. Bu paket, belirlediğiniz viewport boyutlarına göre (örneğin masaüstü ve mobil) sayfanızı tarar ve sadece ilk ekranda görünen elementlerin CSS kurallarını çıkarır.
Kurulum ve temel kullanımı şöyle:
JavaScript:
npm install --save-dev critical
// package.json script'im
"scripts": {
"build:critical": "critical src/index.html --base src/ --inline > dist/index-critical.html"
}
Bu komut çalıştırıldığında, `src/index.html` dosyasını analiz eder, kritik CSS'i bulur ve onu inline `<style>` etiketleri içine yazar, geri kalan CSS bağlantılarını da dönüştürerek yeni bir `dist/index-critical.html` dosyası oluşturur.
Critical aracının çıkardığı sonucu manuel olarak da uygulayabilirsiniz. İşte elde ettiğiniz HTML yapısı şuna benzeyecek:
HTML:
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<title>Süper Hızlı Sitem</title>
<!-- KRİTİK CSS BURADA İNLINE -->
<style>
/ SADECE header, hero, ilk navbar, ilk buton stilleri vs. /
body { font-family: Arial, sans-serif; margin: 0; }
.hero { background: #3498db; color: white; padding: 4rem; text-align: center; }
.btn-primary { background-color: #e74c3c; color: white; padding: 10px 20px; }
/ ... Sadece ilk ekran için gerekli ~10-15KB CSS ... /
</style>
<!-- KRİTİK OLMAYAN CSS ASENKRON YÜKLENİYOR -->
<link rel="preload" href="styles.defer.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.defer.css"></noscript>
</head>
<body>
<div class="hero">...</div>
<!-- Sayfanın geri kalanı -->
<script>
// Fallback için küçük bir script
!function(e){"use strict";var t=function(t,n,o){function i(e){return a.body?e():void setTimeout(function(){i(e)})}var d,r,a=e.document,s=a.createElement("link");if(n)r=n;else{var l=(a.body||a.getElementsByTagName("head")[0]).childNodes;r=l[l.length-1]}var f=a.styleSheets;for(s.rel="stylesheet",s.href=t,s.media="only x",i(function(){r.parentNode.insertBefore(s,n?r:r.nextSibling)}),o=function(e){for(var t=s.href,n=f.length;n--;)if(f[n].href===t)return e();setTimeout(function(){o(e)})},s.addEventListener&&s.addEventListener("load",function(){this.media=o}),s.onloadcssdefined=o,o(function(){s.media!==o&&(s.media=o)}),0<d;i(function(){d--}))}(this);
// Yukarıdaki preload linkini polyfill ile destekle
loadCSS("styles.defer.css");
</script>
</body>
</html>
Buradaki sihir şu: `<style>` etiketi içindeki CSS anında uygulanır, çünkü HTML ile birlikte gelir. `styles.defer.css` dosyası ise `rel="preload"` ve `onload` atribütleri sayesinde render işlemini hiç bloklamadan, sayfa yüklendikten sonra arka planda indirilir ve hazır olduğunda uygulanır. `noscript` etiketi de JavaScript kapalı kullanıcılar için bir fallback sağlar.
Bu tekniği uyguladıktan sonra, orta ölçekli bir projemdeki First Contentful Paint (FCP) süresi neredeyse %40 azaldı. Lighthouse skorum "Performance" alanında 85'lerden 95+ seviyesine fırladı.
Ancak dikkat etmeniz gereken noktalar var:
1. Inline CSS boyutunu küçük tutun. 15KB'ı geçmemeye çalışın. Çok büyük olursa, HTML'nin ilk baytını da geciktirirsiniz.
2. Kritik CSS, her sayfa için farklı olabilir. Ana sayfa, ürün sayfası, blog yazısı... Hepsi için ayrı kritik CSS çıkarmak en iyi sonucu verir.
3. Build sürecinize bu işlemi entegre edin (Webpack, Gulp, vs. pluginleri var). Manuel yapmak sürdürülebilir değil.
Siz de bu yöntemi denediniz mi? Özellikle Next.js, Gatsby gibi framework'lerde veya WordPress temalarında kritik CSS çıkarma konusunda farklı araçlar/plugin'ler mi kullanıyorsunuz? Ya da "Bununla uğraşmaya değmez, CDN ve caching ile hallolur" diyenlerdenseniz, fikirlerinizi merak ediyorum. Yorumlarda buluşalım!