Tarayıcı önceden yükleme tarayıcısının ne olduğunu, performansa nasıl yardımcı olduğunu ve nasıl engel olmayacağınızı öğrenin.
Sayfa hızı optimizasyonunda gözden kaçan bir nokta, tarayıcıların işleyişi hakkında biraz bilgi sahibi olmaktır. Tarayıcılar, performansı geliştiriciler olarak bizim yapamayacağımız şekilde iyileştirmek için belirli optimizasyonlar yapar ancak bu optimizasyonlar yalnızca istemeden engellenmediği sürece geçerlidir.
Anlaşılması gereken dahili tarayıcı optimizasyonlarından biri, tarayıcı önceden yükleme tarayıcısıdır. Bu yayında, önceden yükleme tarayıcısının işleyiş şekli ve daha da önemlisi, bu tarayıcıyı engellemekten nasıl kaçınabileceğiniz açıklanmaktadır.
Önceden yükleme tarayıcısı nedir?
Her tarayıcıda, ham işaretlemeyi token'lara ayıran ve bir nesne modeline dönüştüren birincil bir HTML ayrıştırıcı bulunur. Bu işlem, ayrıştırıcı engelleyici bir kaynak bulduğunda duraklatılana kadar devam eder. Örneğin, <link> öğesiyle yüklenen bir stil sayfası veya async ya da defer özelliği olmadan <script> öğesiyle yüklenen bir komut dosyası.
<link> öğesiyle karşılaşır. Bu öğe, CSS indirilip ayrıştırılana kadar tarayıcının belgenin geri kalanını ayrıştırmasını, hatta herhangi bir bölümünü oluşturmasını engeller.
CSS dosyalarında, stilsiz içerik parlamasını (FOUC) önlemek için oluşturma engellenir. Bu, bir sayfanın stilsiz sürümünün, stiller uygulanmadan önce kısa bir süre görülebildiği durumdur.
Tarayıcı, defer veya async özelliği olmayan <script> öğeleriyle karşılaştığında sayfanın ayrıştırılmasını ve oluşturulmasını da engeller.
Bunun nedeni, birincil HTML ayrıştırıcısı çalışmaya devam ederken tarayıcının, belirli bir komut dosyasının DOM'u değiştirip değiştirmeyeceğini kesin olarak bilememesidir. Bu nedenle, JavaScript'inizi belgenin sonuna yüklemek yaygın bir uygulamadır. Böylece, engellenen ayrıştırma ve oluşturma işlemlerinin etkileri önemsiz hale gelir.
Bunlar, tarayıcının hem ayrıştırmayı hem de oluşturmayı engellemesi gerektiğinin iyi nedenleridir. Ancak bu önemli adımlardan birinin engellenmesi, diğer önemli kaynakların keşfini geciktirerek gösteriyi engelleyebileceğinden istenmeyen bir durumdur. Neyse ki tarayıcılar, önceden yükleme tarayıcısı adı verilen ikincil bir HTML ayrıştırıcı aracılığıyla bu sorunları azaltmak için ellerinden geleni yapar.
<body> öğesindeki resim işaretlemesini işlemeye başlamadan önce CSS'yi yükleyip işlediği için engellenir. Ancak önceden yükleme tarayıcısı, ham işaretlemede ileriye bakarak bu resim kaynağını bulabilir ve birincil HTML ayrıştırıcının engellemesi kaldırılmadan önce yüklemeye başlayabilir.
Ön yükleme tarayıcısının rolü tahmine dayalıdır. Yani, birincil HTML ayrıştırıcısının keşfetmesinden önce kaynakları fırsatçı bir şekilde getirmek için ham işaretlemeyi inceler.
Önceden yükleme tarayıcısının ne zaman çalıştığını anlama
Önceden yükleme tarayıcısı, engellenen oluşturma ve ayrıştırma nedeniyle vardır. Bu iki performans sorunu hiç olmasaydı önceden yükleme tarayıcısı pek kullanışlı olmazdı. Bir web sayfasının önceden yükleme tarayıcısından yararlanıp yararlanmadığını anlamanın anahtarı, bu engelleme olaylarına bağlıdır. Bunu yapmak için, önceden yükleme tarayıcısının nerede çalıştığını öğrenmek amacıyla isteklere yapay bir gecikme ekleyebilirsiniz.
Örnek olarak stil sayfası içeren temel metin ve resimlerden oluşan bu sayfayı inceleyin. CSS dosyaları hem oluşturmayı hem de ayrıştırmayı engellediğinden, bir proxy hizmeti aracılığıyla stil sayfası için iki saniyelik yapay bir gecikme uygularsınız. Bu gecikme, önceden yükleme tarayıcısının çalıştığı ağ şelalesini görmeyi kolaylaştırır.
Şelalede görebileceğiniz gibi, önceden yükleme tarayıcısı oluşturma ve doküman ayrıştırma engellenmiş olsa bile <img> öğesini bulur. Bu optimizasyon olmadan tarayıcı, engelleme döneminde fırsatçı bir şekilde öğeleri getiremez ve daha fazla kaynak isteği eşzamanlı değil, ardışık olur.
Bu basit örneği bir kenara bırakıp önceden yükleme tarayıcısının başarısız olabileceği bazı gerçek dünya kalıplarına ve bunları düzeltmek için neler yapılabileceğine göz atalım.
async komut dosyası yerleştirildi
<head> içinde aşağıdaki gibi satır içi JavaScript içeren bir HTML'niz olduğunu varsayalım:
<script>
const scriptEl = document.createElement('script');
scriptEl.src = '/yall.min.js';
document.head.appendChild(scriptEl);
</script>
Yerleştirilen komut dosyaları varsayılan olarak async olduğundan bu komut dosyası yerleştirildiğinde async özelliği uygulanmış gibi davranır. Bu, komut dosyasının mümkün olan en kısa sürede çalıştırılacağı ve oluşturmayı engellemeyeceği anlamına gelir. Kulağa ideal geliyor, değil mi? Ancak bu satır içi <script> öğesinin, harici bir CSS dosyası yükleyen <link> öğesinden sonra geldiğini varsayarsanız ideal olmayan bir sonuç elde edersiniz:
async komut dosyası var. Önceden yükleme tarayıcısı, istemciye yerleştirildiği için oluşturmayı engelleme aşamasında komut dosyasını bulamaz.
Burada ne olduğuna bakalım:
- 0. saniyede ana doküman istenir.
- 1,4 saniyede, gezinme isteğinin ilk baytı gelir.
- 2, 0 saniyede CSS ve resim istenir.
- Ayrıştırıcı, stil sayfasını yüklerken engellendiği ve
asynckomut dosyasını ekleyen satır içi JavaScript, 2,6 saniyede bu stil sayfasından sonra geldiği için komut dosyasının sağladığı işlev mümkün olan en kısa sürede kullanılamaz.
Komut dosyası isteği yalnızca stil sayfasının indirilmesi tamamlandıktan sonra gerçekleştiği için bu durum ideal değildir. Bu, komut dosyasının mümkün olan en kısa sürede çalıştırılmasını geciktirir. Buna karşılık, <img> öğesi sunucu tarafından sağlanan işaretlemede bulunabildiğinden önceden yükleme tarayıcısı tarafından keşfedilir.
Peki, komut dosyasını DOM'a eklemek yerine async özelliğiyle normal bir <script> etiketi kullanırsanız ne olur?
<script src="/yall.min.js" async></script>
Sonuç şu şekildedir:
async <script> öğesi var. Önceden yükleme tarayıcısı, oluşturmayı engelleyen aşama sırasında komut dosyasını keşfeder ve CSS ile aynı anda yükler.
Bu sorunların rel=preload kullanılarak giderilebileceğini düşünmek cazip gelebilir. Bu yöntem kesinlikle işe yarar ancak bazı yan etkileri olabilir. Sonuçta, DOM'a <script> öğesi eklemeyerek önlenebilecek bir sorunu düzeltmek için neden rel=preload kullanılsın?
async komut dosyası var ancak async komut dosyası daha erken keşfedilmesi için önceden yüklenmiş.
Önceden yükleme, buradaki sorunu "düzeltir" ancak yeni bir sorun ortaya çıkarır: İlk iki demoda yer alan async komut dosyası, <head> içinde yüklenmesine rağmen "Düşük" öncelikte, stil sayfası ise "En Yüksek" öncelikte yüklenir. async komut dosyasının önceden yüklendiği son demoda, stil sayfası hâlâ "En yüksek" öncelikte yüklenir ancak komut dosyasının önceliği "Yüksek" olarak yükseltilmiştir.
Bir kaynağın önceliği yükseltildiğinde tarayıcı, kaynağa daha fazla bant genişliği ayırır. Bu, stil sayfasının en yüksek önceliğe sahip olmasına rağmen komut dosyasının yükseltilmiş önceliğinin bant genişliği çekişmesine neden olabileceği anlamına gelir. Bu durum, yavaş bağlantılarda veya kaynakların oldukça büyük olduğu durumlarda bir faktör olabilir.
Bu sorunun cevabı basittir: Başlatma sırasında bir komut dosyası gerekiyorsa bunu DOM'a ekleyerek önceden yükleme tarayıcısını devre dışı bırakmayın. <script> öğesinin yerleşiminin yanı sıra defer ve async gibi özelliklerle de gerektiği gibi denemeler yapın.
JavaScript ile geç yükleme
Geç yükleme, veri tasarrufu sağlamak için mükemmel bir yöntemdir ve genellikle resimlere uygulanır. Ancak bazen geç yükleme, tabiri caizse "ekranın üst kısmındaki" resimlere yanlış bir şekilde uygulanır.
Bu durum, önceden yükleme tarayıcısı söz konusu olduğunda kaynakların bulunabilirliğiyle ilgili olası sorunlara yol açar ve bir resme yapılan referansın bulunması, indirilmesi, kodunun çözülmesi ve sunulması için gereken süreyi gereksiz yere uzatabilir. Örnek olarak şu resim işaretlemesini ele alalım:
<img data-src="/sand-wasp.jpg" alt="Sand Wasp" width="384" height="255">
data- önekinin kullanımı, JavaScript destekli geç yükleyicilerde yaygın bir kalıptır. Resim, görüntü alanına kaydırıldığında geç yükleyici data- önekini kaldırır. Yani önceki örnekte data-src, src olur. Bu güncelleme, tarayıcıdan kaynağı getirmesini ister.
Bu kalıp, başlangıç sırasında görüntü alanında bulunan resimlere uygulanmadığı sürece sorun oluşturmaz. Önceden yükleme tarayıcısı, data-src özelliğini src (veya srcset) özelliğiyle aynı şekilde okumadığından resim referansı daha önce keşfedilmez. Daha da kötüsü, geç yükleyici JavaScript indirilip derlenip yürütülene kadar resmin yüklenmesi gecikir.
Resmin boyutuna bağlı olarak (görüntü alanının boyutuna bağlı olabilir) Largest Contentful Paint (LCP) için aday bir öğe olabilir. Önceden yükleme tarayıcısı, resim kaynağını tahmine dayalı olarak önceden getiremediğinde(büyük olasılıkla sayfanın stil sayfalarının oluşturmayı engellediği noktada) LCP olumsuz etkilenir.
Çözüm, resim işaretlemesini değiştirmektir:
<img src="/sand-wasp.jpg" alt="Sand Wasp" width="384" height="255">
Önceden yükleme tarayıcısı, resim kaynağını daha hızlı keşfedip getireceğinden, başlangıç sırasında görüntü alanında bulunan resimler için en uygun kalıp budur.
Bu basitleştirilmiş örnekteki sonuç, yavaş bir bağlantıda LCP'de 100 milisaniyelik bir iyileştirmedir. Bu çok büyük bir iyileştirme gibi görünmeyebilir ancak çözümün hızlı bir işaretleme düzeltmesi olduğunu ve çoğu web sayfasının bu örnek grubundan daha karmaşık olduğunu düşündüğünüzde bunun büyük bir iyileştirme olduğunu anlarsınız. Bu, LCP adaylarının bant genişliği için diğer birçok kaynakla rekabet etmesi gerekebileceği anlamına gelir. Bu nedenle, bu tür optimizasyonlar giderek daha önemli hale gelir.
CSS arka plan resimleri
Tarayıcı önceden yükleme tarayıcısının işaretlemeyi taradığını unutmayın. background-image özelliği tarafından referans verilen resimlerin getirilmesini içerebilecek CSS gibi diğer kaynak türlerini taramaz.
HTML gibi, tarayıcılar da CSS'yi CSSOM olarak bilinen kendi nesne modeline dönüştürür. CSSOM oluşturulurken harici kaynaklar keşfedilirse bu kaynaklar önceden yükleme tarayıcısı tarafından değil, keşif sırasında istenir.
Sayfanızın LCP adayı, CSS background-image özelliğine sahip bir öğe olsun. Kaynaklar yüklenirken şunlar gerçekleşir:
background-image özelliğine sahip bir öğedir (3. satır). İstediği resim, CSS ayrıştırıcısı tarafından bulunana kadar getirilmeye başlamaz.
Bu durumda, önceden yükleme tarayıcısı yenilgiye uğramaktan ziyade devre dışı kalır. Yine de sayfadaki bir LCP adayı background-image CSS özelliğinden geliyorsa bu resmi önceden yüklemeniz gerekir:
<!-- Make sure this is in the <head> below any
stylesheets, so as not to block them from loading -->
<link rel="preload" as="image" href="lcp-image.jpg">
Bu rel=preload ipucu küçüktür ancak tarayıcının resmi normalden daha erken keşfetmesine yardımcı olur:
background-image özelliğine sahip bir öğedir (3. satır). rel=preload ipucu, tarayıcının resmi ipucu olmadan olduğundan yaklaşık 250 milisaniye daha erken keşfetmesine yardımcı olur.
rel=preload ipucu sayesinde LCP adayı daha erken keşfedilir ve LCP süresi kısalır. Bu ipucu sorunu çözmeye yardımcı olsa da daha iyi bir seçenek, resim LCP adayınızın CSS'den yüklenmesi gerekip gerekmediğini değerlendirmek olabilir. <img> etiketiyle, önceden yükleme tarayıcısının resmi keşfetmesine izin verirken görüntü alanına uygun bir resim yükleme konusunda daha fazla kontrole sahip olursunuz.
Çok fazla kaynağı satır içine alma
Satır içi yerleştirme, bir kaynağı HTML'nin içine yerleştiren bir uygulamadır. <style> öğelerindeki stil sayfalarını, <script> öğelerindeki komut dosyalarını ve base64 kodlaması kullanan diğer tüm kaynakları satır içine alabilirsiniz.
Kaynak için ayrı bir istek gönderilmediğinden, kaynakları satır içine almak indirmekten daha hızlı olabilir. Doğrudan dokümanda yer alır ve anında yüklenir. Ancak önemli dezavantajları vardır:
- HTML'nizi önbelleğe almıyorsanız (HTML yanıtı dinamikse önbelleğe alamazsınız) satır içi kaynaklar hiçbir zaman önbelleğe alınmaz. Satır içi kaynaklar yeniden kullanılamadığından bu durum performansı etkiler.
- HTML'yi önbelleğe alabilseniz bile satır içi kaynaklar dokümanlar arasında paylaşılmaz. Bu durum, önbelleğe alınabilen ve tüm kaynakta yeniden kullanılabilen harici dosyalara kıyasla önbelleğe alma verimliliğini azaltır.
- Çok fazla satır içi öğe kullanırsanız önceden yükleme tarayıcısının dokümanın ilerleyen kısımlarındaki kaynakları keşfetmesini geciktirirsiniz. Bunun nedeni, ek satır içi içeriğin indirilmesinin daha uzun sürmesidir.
Bu sayfayı örnek olarak ele alalım. Belirli koşullarda LCP adayı, sayfanın üst kısmındaki resimdir ve CSS, <link> öğesi tarafından yüklenen ayrı bir dosyada bulunur. Sayfada ayrıca CSS kaynağından ayrı dosyalar olarak istenen dört web yazı tipi kullanılır.
<img> öğesinden yüklenen bir resimdir ancak sayfa yükleme için gereken CSS ve yazı tipleri ayrı kaynaklarda yüklendiğinden önceden yükleme tarayıcısı tarafından keşfedilir. Bu durum, önceden yükleme tarayıcısının işini yapmasını geciktirmez.
Peki CSS ve tüm yazı tipleri base64 kaynakları olarak satır içine alınırsa ne olur?
<img> öğesinden yüklenen bir resimdir ancak CSS'nin ve dört yazı tipi kaynağının `` içinde satır içi olarak eklenmesi, önceden yükleme tarayıcısının resmi keşfetmesini bu kaynaklar tamamen indirilene kadar geciktirir.
Satır içi yerleştirmenin etkisi, bu örnekte LCP ve genel olarak performans açısından olumsuz sonuçlar doğurur. Hiçbir şeyi satır içine almayan sayfa sürümü, LCP resmini yaklaşık 3,5 saniyede boyar. Her şeyi satır içine alan sayfa, LCP resmini 7 saniyeden biraz daha uzun bir süre boyunca boyamaz.
Burada önceden yükleme tarayıcısından daha fazlası söz konusudur. Base64, ikili kaynaklar için verimli bir biçim olmadığından satır içi yazı tipleri iyi bir strateji değildir. CSSOM tarafından gerekli olduğu belirlenmediği sürece harici yazı tipi kaynaklarının indirilmemesi de başka bir etkendir. Bu yazı tipleri base64 olarak satır içine alındığında, mevcut sayfa için gerekli olup olmadıklarına bakılmaksızın indirilirler.
Önceden yükleme bu durumda işe yarayabilir mi? Elbette. LCP resmini önceden yükleyerek LCP süresini kısaltabilirsiniz ancak önbelleğe alınamayabilecek HTML'nizi satır içi kaynaklarla şişirmenin başka olumsuz performans sonuçları vardır. First Contentful Paint (FCP) de bu kalıptan etkilenir. Sayfanın satır içi öğe içermeyen sürümünde FCP yaklaşık 2, 7 saniyedir. Her şeyin satır içi olduğu sürümde FCP yaklaşık 5, 8 saniyedir.
HTML'ye satır içi öğeler, özellikle de base64 kodlu kaynaklar eklerken çok dikkatli olun. Genel olarak, çok küçük kaynaklar dışında önerilmez. Mümkün olduğunca az satır içi kod kullanın. Çok fazla satır içi kod kullanmak tehlikelidir.
İstemci tarafı JavaScript ile işaretleme oluşturma
JavaScript'in sayfa hızını kesinlikle etkilediği konusunda şüphe yoktur. Geliştiriciler, etkileşim sağlamak için JavaScript'e güvenir. Ayrıca, içeriğin kendisini sunmak için de JavaScript'e güvenme eğilimi vardır. Bu durum, bazı açılardan daha iyi bir geliştirici deneyimi sunsa da geliştiricilere sağlanan avantajlar her zaman kullanıcılar için de avantaj sağlamaz.
Önceden yükleme tarayıcısını engelleyebilecek bir kalıp, istemci tarafı JavaScript ile işaretleme oluşturmaktır:
İşaretleme yükleri tarayıcıda tamamen JavaScript tarafından içerildiğinde ve oluşturulduğunda, bu işaretlemedeki tüm kaynaklar önceden yükleme tarayıcısı tarafından etkili bir şekilde görünmez. Bu durum, önemli kaynakların keşfedilmesini geciktirir ve LCP'yi kesinlikle etkiler. Bu örneklerde, LCP resminin isteği, görünmesi için JavaScript gerektirmeyen eşdeğer sunucu tarafından oluşturulan deneyime kıyasla önemli ölçüde gecikir.
Bu konu, bu makalenin odağından biraz uzaklaşıyor ancak istemcide işaretleme oluşturmanın etkileri, önceden yükleme tarayıcısını yenmenin çok ötesine geçiyor. Öncelikle, JavaScript gerektirmeyen bir deneyimi desteklemek için JavaScript kullanmak, Interaction to Next Paint (INP) metriğini etkileyebilecek gereksiz işlem süresine neden olur. İstemcide çok büyük miktarda işaretleme oluşturmak, sunucu tarafından gönderilen aynı miktarda işaretlemeye kıyasla uzun süren görevler oluşturma olasılığını artırır. JavaScript'in gerektirdiği ek işlemenin yanı sıra bunun nedeni, tarayıcıların sunucudan işaretlemeyi akışla aktarması ve oluşturmayı uzun süren görevleri sınırlayacak şekilde parçalara ayırmasıdır. Öte yandan, istemci tarafından oluşturulan işaretleme tek bir monolitik görev olarak işlenir ve bu durum sayfanın INP'sini etkileyebilir.
Bu senaryonun çözümü şu sorunun yanıtına bağlıdır: Sayfanızın işaretlemesinin istemcide oluşturulmak yerine sunucu tarafından sağlanamamasının bir nedeni var mı? Bu sorunun yanıtı "hayır" ise mümkün olduğunda sunucu tarafı oluşturma (SSR) veya statik olarak oluşturulan işaretleme kullanılmalıdır. Bu, önceden yükleme tarayıcısının önemli kaynakları önceden keşfedip fırsat buldukça getirmesine yardımcı olur.
Sayfanızın, sayfa işaretlemenizin bazı bölümlerine işlev eklemek için JavaScript'e ihtiyacı varsa bunu SSR ile, vanilla JavaScript kullanarak veya her iki dünyanın en iyi özelliklerini elde etmek için hydration ile yapabilirsiniz.
Önceden yükleme tarayıcısının size yardımcı olmasını sağlayın
Önceden yükleme tarayıcısı, başlangıçta sayfaların daha hızlı yüklenmesine yardımcı olan son derece etkili bir tarayıcı optimizasyonudur. Önemli kaynakları önceden keşfetme yeteneğini engelleyen kalıplardan kaçınarak yalnızca kendiniz için geliştirmeyi kolaylaştırmakla kalmaz, aynı zamanda bazı web vitals da dahil olmak üzere birçok metrikte daha iyi sonuçlar sağlayacak daha iyi kullanıcı deneyimleri oluşturursunuz.
Özetlemek gerekirse bu yayından çıkarmanız gerekenler şunlardır:
- Tarayıcı önceden yükleme tarayıcısı, birincil HTML ayrıştırıcının engellenmesi durumunda daha erken getirebileceği kaynakları keşfetmek için birincil ayrıştırıcıdan önce tarama yapan ikincil bir HTML ayrıştırıcıdır.
- İlk gezinme isteğinde sunucu tarafından sağlanan işaretlemede bulunmayan kaynaklar, önceden yükleme tarayıcısı tarafından bulunamaz. Önceden yükleme tarayıcısının engellenmesine neden olabilecek durumlar şunlardır (ancak bunlarla sınırlı değildir):
- JavaScript ile DOM'a kaynak ekleme (komut dosyaları, resimler, stil sayfaları veya sunucudan gelen ilk işaretleme yükünde daha iyi olacak başka herhangi bir şey).
- JavaScript çözümü kullanarak ekranın üst kısmındaki resimleri veya iframe'leri geç yükleme.
- JavaScript kullanarak doküman alt kaynaklarına referanslar içerebilecek işaretlemeyi istemcide oluşturma.
- Önceden yükleme tarayıcısı yalnızca HTML'yi tarar. LCP adayları da dahil olmak üzere önemli öğelere referanslar içerebilecek diğer kaynakların (özellikle CSS) içeriğini incelemez.
Herhangi bir nedenle, önceden yükleme tarayıcısının yükleme performansını hızlandırma özelliğini olumsuz etkileyen bir kalıptan kaçınamıyorsanız rel=preload kaynak ipucunu kullanabilirsiniz. rel=preload kullanıyorsanız istediğiniz etkiyi elde ettiğinizden emin olmak için laboratuvar araçlarında test edin. Son olarak, çok fazla kaynağı önceden yüklemeyin. Her şeye öncelik verirseniz hiçbir şey öncelikli olmaz.
Kaynaklar
- Komut dosyası tarafından yerleştirilen "eş zamansız komut dosyaları" zararlı olarak kabul edilir
- Tarayıcı ön yükleyicisi sayfaların daha hızlı yüklenmesini nasıl sağlar?
- Yükleme hızını artırmak için kritik öğeleri önceden yükleme
- Algılanan sayfa hızını artırmak için ağ bağlantılarını erkenden kurun
- Largest Contentful Paint'i optimize etme