Merhaba arkadaşlar, bugün performans optimizasyonu yaparken başımı çok ağrıtan bir sorundan ve onun temiz çözümünden bahsedeceğim. Hepimizin bildiği gibi, CSS dosyaları render engelleyici (render-blocking) kaynaklar. Tarayıcı, CSS'yi indirip işlemeden DOM'u render edemiyor. Bu da özellikle mobilde veya yavaş ağlarda First Contentful Paint (FCP) süresini ciddi anlamda uzatıyor. Ben de bu sorunu `<link rel="preload">` ile nasıl aştığımı anlatacağım.
Sorunun Can Alıcı Noktası
Projemde oldukça büyük bir ana CSS dosyam ve birkaç tane de component kütüphanesinden gelen CSS vardı. Lighthouse ve PageSpeed Insights raporları sürekli "Eliminate render-blocking resources" uyarısı veriyordu. Kritik CSS çıkarma (critical CSS extraction) işlemi yapmıştım ama dinamik olarak yüklenen component'ların stilleri için bu yeterli olmuyordu. CSS'yi asenkron yüklemek için `preload`dan daha farklı yöntemler de var ama benim ihtiyacım olan şey, tarayıcıya "Bu dosya çok önemli, hemen indirmeye başla ama render'ı bekleterek yükle" talimatı vermekti.
Preload ile Erken Yükleme Mantığı
`<link rel="preload">` bize bir kaynağın sayfa yaşam döngüsünün (lifecycle) çok erken bir aşamasında, tarayıcının kendi önceliklendirme mantığını beklemeden yüklenmesini sağlar. Aslında tarayıcıya "Buna ihtiyacım olacak, şimdiden hazırla" demiş oluyorsunuz. CSS için kullanıldığında, tarayıcı bu dosyayı yüklemeye hemen başlıyor ve böylece daha sonra parser o satıra geldiğinde CSS çoktan indirilmiş (veya indiriliyor) oluyor. Bu da render engelinin süresini kısaltıyor.
İşte benim ana CSS dosyam için kullandığım temel yapı:
Buradaki sihir, `onload` olayında yatıyor. Tarayıcı önce dosyayı yüklemeye başlıyor (`rel="preload"`). Dosya yüklendiğinde, `onload` tetikleniyor ve `rel` özniteliği `'stylesheet'` olarak değiştiriliyor. Bu sayede CSS artık uygulanıyor. `this.onload=null` ise olayın tekrar tetiklenmemesi için bir temizlik. `<noscript>` etiketi de JavaScript kapalı olan kullanıcılar için bir fallback sağlıyor.
Webpack veya Vite ile Entegrasyon
Elbette her CSS dosyası için bu kodu manuel yazmak pratik değil. Ben projemde (Vite kullanıyorum) bunu otomatik hale getirdim. Eğer Webpack kullanıyorsanız, `preload-webpack-plugin` gibi eklentileri inceleyebilirsiniz. Benim tercihim, sadece gerçekten kritik olan ilk CSS dosyasını preload yapmak oldu. Çok fazla CSS'yi preload yapmak, diğer önemli kaynakların (fontlar, ilk ekran resimleri) yüklenmesini geciktirebilir.
Vite'da `index.html` içinde şöyle bir yönlendirme yapabilirsiniz:
Ya da eğer CSS'niz bir JS dosyası içinden import ediliyorsa, build sırasında Vite/Webpack bu chunk'lar için otomatik preload link'leri oluşturabilir. Anahtar nokta, as="style" attribute'unu doğru belirlemek. Yoksa tarayıcı bu kaynağın ne olduğunu anlayamaz ve doğru önceliklendirmeyi yapamaz.
Sonuçlar ve Çıkarımlar
Bu değişikliği yaptıktan sonra, özellikle yavaş 3G bağlantı simülasyonlarında FCP süresinde gözle görülür bir iyileşme oldu. Lighthouse puanım, "Eliminate render-blocking resources" maddesinden artı puan aldı. Ancak dikkat edilmesi gereken bir husus var: Eğer CSS dosyanız çok büyükse, preload yapmak TCP bağlantısını meşgul edip Largest Contentful Paint (LCP)'yi olumsuz etkileyebilir. Bu yüzden önce CSS'nizi minify etmek, gereksiz kodları temizlemek (PurgeCSS gibi araçlarla) ve mümkünse critical path CSS çıkarmak her zaman ilk adımınız olmalı. `preload` ise bu iyileştirmelerin üzerine konacak son dokunuş.
Siz projelerinizde render engelleyici kaynakları elemek için hangi yöntemleri kullanıyorsunuz? `<link rel="preload">` dışında CSS yükleme stratejisi olarak `preconnect` veya `loadCSS` gibi polyfill'leri deneyen oldu mu? Tecrübelerinizi yorumlarda paylaşırsanız çok sevinirim!
Projemde oldukça büyük bir ana CSS dosyam ve birkaç tane de component kütüphanesinden gelen CSS vardı. Lighthouse ve PageSpeed Insights raporları sürekli "Eliminate render-blocking resources" uyarısı veriyordu. Kritik CSS çıkarma (critical CSS extraction) işlemi yapmıştım ama dinamik olarak yüklenen component'ların stilleri için bu yeterli olmuyordu. CSS'yi asenkron yüklemek için `preload`dan daha farklı yöntemler de var ama benim ihtiyacım olan şey, tarayıcıya "Bu dosya çok önemli, hemen indirmeye başla ama render'ı bekleterek yükle" talimatı vermekti.
`<link rel="preload">` bize bir kaynağın sayfa yaşam döngüsünün (lifecycle) çok erken bir aşamasında, tarayıcının kendi önceliklendirme mantığını beklemeden yüklenmesini sağlar. Aslında tarayıcıya "Buna ihtiyacım olacak, şimdiden hazırla" demiş oluyorsunuz. CSS için kullanıldığında, tarayıcı bu dosyayı yüklemeye hemen başlıyor ve böylece daha sonra parser o satıra geldiğinde CSS çoktan indirilmiş (veya indiriliyor) oluyor. Bu da render engelinin süresini kısaltıyor.
İşte benim ana CSS dosyam için kullandığım temel yapı:
HTML:
<!-- Normal, render'ı engelleyen link etiketi -->
<link rel="stylesheet" href="/assets/css/main.min.css">
<!-- Preload ile optimize edilmiş hali -->
<link rel="preload" href="/assets/css/main.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/assets/css/main.min.css"></noscript>
Buradaki sihir, `onload` olayında yatıyor. Tarayıcı önce dosyayı yüklemeye başlıyor (`rel="preload"`). Dosya yüklendiğinde, `onload` tetikleniyor ve `rel` özniteliği `'stylesheet'` olarak değiştiriliyor. Bu sayede CSS artık uygulanıyor. `this.onload=null` ise olayın tekrar tetiklenmemesi için bir temizlik. `<noscript>` etiketi de JavaScript kapalı olan kullanıcılar için bir fallback sağlıyor.
Elbette her CSS dosyası için bu kodu manuel yazmak pratik değil. Ben projemde (Vite kullanıyorum) bunu otomatik hale getirdim. Eğer Webpack kullanıyorsanız, `preload-webpack-plugin` gibi eklentileri inceleyebilirsiniz. Benim tercihim, sadece gerçekten kritik olan ilk CSS dosyasını preload yapmak oldu. Çok fazla CSS'yi preload yapmak, diğer önemli kaynakların (fontlar, ilk ekran resimleri) yüklenmesini geciktirebilir.
Vite'da `index.html` içinde şöyle bir yönlendirme yapabilirsiniz:
HTML:
<link rel="modulepreload" href="/src/main.js" />
<link rel="preload" href="/src/assets/css/critical.css" as="style" />
Ya da eğer CSS'niz bir JS dosyası içinden import ediliyorsa, build sırasında Vite/Webpack bu chunk'lar için otomatik preload link'leri oluşturabilir. Anahtar nokta, as="style" attribute'unu doğru belirlemek. Yoksa tarayıcı bu kaynağın ne olduğunu anlayamaz ve doğru önceliklendirmeyi yapamaz.
Bu değişikliği yaptıktan sonra, özellikle yavaş 3G bağlantı simülasyonlarında FCP süresinde gözle görülür bir iyileşme oldu. Lighthouse puanım, "Eliminate render-blocking resources" maddesinden artı puan aldı. Ancak dikkat edilmesi gereken bir husus var: Eğer CSS dosyanız çok büyükse, preload yapmak TCP bağlantısını meşgul edip Largest Contentful Paint (LCP)'yi olumsuz etkileyebilir. Bu yüzden önce CSS'nizi minify etmek, gereksiz kodları temizlemek (PurgeCSS gibi araçlarla) ve mümkünse critical path CSS çıkarmak her zaman ilk adımınız olmalı. `preload` ise bu iyileştirmelerin üzerine konacak son dokunuş.
Siz projelerinizde render engelleyici kaynakları elemek için hangi yöntemleri kullanıyorsunuz? `<link rel="preload">` dışında CSS yükleme stratejisi olarak `preconnect` veya `loadCSS` gibi polyfill'leri deneyen oldu mu? Tecrübelerinizi yorumlarda paylaşırsanız çok sevinirim!