Siteniz neden daha küçük değil, daha az istek göndermeli
Küçük dosyalar ücretsiz değildir. Modern HTTP istek ek yükünü küçülttü, önemsiz hâle getirmedi.
İçindekiler
- “Her dosyayı küçült yeter” rahatlatıcı miti
- İstekler yalnızca byte değildir
- “Ama HTTP/2 bunu çözmüştü,” çoğunlukla hayır
- Gerçek waterfall’da yaşar
- Daha küçük dosyalar hâlâ önemlidir — sadece eşit ölçüde değil
- Çok sayıda küçük dosyanın gizli maliyetleri
- 1. Geç keşif
- 2. Header ek yükü
- 3. Main-thread kesintisi
- 4. Cache karmaşıklığı
- Bundling geri döndü, ama muhakemeyle
- Üçüncü taraf istekler ekstra şüpheyi hak eder
- Pratik bir istek azaltma kontrol listesi
- Kaldırın
- Düşünerek birleştirin
- Erteleyin
- Doğru cache’leyin
- Yeniden ölçün
- İyi olan nasıl görünür
“Her dosyayı küçült yeter” rahatlatıcı miti
Yıllar boyunca web performansı tavsiyesi basit duyuldu: her şeyi sıkıştır, her şeyi minify et, her varlığı küçült.
Bu tavsiye hâlâ genel olarak doğru. 40 KB’lık bir script genellikle 400 KB’lık bir script’ten iyidir. Optimize edilmiş bir görsel, ham export’tan iyidir. Brotli, AVIF, CSS minification, tree shaking ve font subsetting önemlidir.
Ancak birçok production sitesinde daha büyük sorun artık tek bir aşırı büyük dosya değildir. Sorun, sayfa kullanılabilir hissettirmeden önce tarayıcının istemek zorunda kaldığı şeylerin sayısıdır.
Bir sayfa dosya boyutları açısından disiplinli görünebilir ve yine de yavaş olabilir; çünkü 120 istek gönderir: CSS parçaları, JavaScript chunk’ları, üçüncü taraf tag’ler, font dosyaları, tracking pixel’leri, icon sprite’ları, JSON endpoint’leri, preload’lar, analytics beacon’ları ve cache revalidation’ları. Her biri “küçük” olabilir. Birlikte uzun ve kırılgan bir waterfall oluştururlar.
Pratik kural şudur: tekil varlıklar makul ölçüde sıkıştırıldıktan sonra, istek sayısını azaltmak çoğu zaman her dosyadan birkaç kilobayt tıraşlamaktan daha fazla kullanıcı deneyimi iyileştirir.
İstekler yalnızca byte değildir
Bir ağ isteği yalnızca veri aktarımı değildir. Bir iş dizisidir.
Tarayıcı kaynağı keşfetmek, ne zaman getireceğine karar vermek, diğer kaynaklara karşı zamanlamak, header’ları göndermek, sunucuyu beklemek, header’ları almak, yanıtı ayrıştırmak, çoğu zaman açmak ve sonra onunla yararlı bir şey yapmak zorundadır.
Bu “yararlı şey” pahalı olabilir. Bir JavaScript dosyasının ayrıştırılması, derlenmesi ve çalıştırılması gerekir. Bir CSS dosyası render’ı engelleyebilir. Bir font dosyası okunabilir metni geciktirebilir veya layout shift’e neden olabilir. Bir görsel Largest Contentful Paint öğesini etkileyebilir. Üçüncü taraf bir script kendi bağımlılık zincirini getirebilir.
Dosyalar küçük olduğunda bile istek sayısının hâlâ önemli olmasının nedeni budur. 3 KB’lık bir script, render’ı engelliyorsa, geç geliyorsa ve ana thread üzerinde yanlış anda çalışıyorsa 30 KB’lık bir görselden daha kötü olabilir.
Bir Lighthouse raporu okuyor ve onlarca ayrı uyarı tarafından cezalandırılmış gibi hissediyorsanız, tek tek skorlardan önce istek waterfall’una bakarak başlayın. Paniklemeden Lighthouse raporu okuma konusunda pratik bir rehberimiz var; ama kısa hâli şudur: ilk render’ı neyin engellediğini ve ana içeriği neyin geciktirdiğini bulun.
“Ama HTTP/2 bunu çözmüştü,” çoğunlukla hayır
HTTP/2 ve HTTP/3 isteklerin ekonomisini değiştirdi. Multiplexing, header compression ve daha iyi bağlantı davranışı getirdiler. Basitçe söylemek gerekirse, tarayıcılar daha az bağlantı üzerinden birden fazla istek göndermede çok daha iyi hâle geldi.
Bu gerçek bir iyileşmeydi. Ayrıca yalnızca bağlantı sınırlarından kaçınmak için yapılan aşırı CSS sprite’ları ve devasa birleştirilmiş bundle’lar gibi bazı eski alışkanlıkları da bitirdi.
Ama HTTP/2 istekleri ücretsiz yapmadı.
Multiplexing, birçok kaynak bir bağlantıyı paylaştığında yardımcı olur; ancak tarayıcının bunları hâlâ önceliklendirmesi gerekir. Sunucuların hâlâ yanıt vermesi gerekir. İstemcinin hâlâ her yanıtı işlemesi gerekir. Congestion, packet loss, TLS negotiation, DNS lookup, cache miss’leri ve main-thread baskısı hâlâ vardır.
HTTP/3 özellikle connection migration ve transport katmanındaki head-of-line blocking çevresinde bazı transport davranışlarını iyileştirir. Kaynakları keşfetme, zamanlama, indirme, ayrıştırma ve çalıştırma maliyetini ortadan kaldırmaz.
Bu yüzden modern hedef “her şeyi tek bir dev dosyada bundle etmek” değildir. Hedef “daha az kritik istek göndermek ve kalan istekleri bilinçli hâle getirmek”tir.
Gerçek waterfall’da yaşar
Performans sorunları kendilerini nadiren tek bir metrikte duyurur. Şekil olarak görünürler.
Bir tarayıcı network panelini açın ve ilk birkaç saniyeye bakın. Şunları sorun:
- Ana içerik görünmeden önce kaç istek başlıyor?
- Hangi istekler render’ı engelliyor?
- Önemli kaynaklar geç mi keşfediliyor?
- Üçüncü taraf script’ler birinci taraf CSS, font veya görsellerle rekabet ediyor mu?
- Birçok dosya doğrudan cache’ten sunulmak yerine 304 yanıtı mı döndürüyor?
- İkonlar, fontlar veya UI parçaları sayfanın ihtiyaç duyduğundan daha fazla dosyaya mı bölünmüş?
Hızlı bir sayfanın erken waterfall’u genellikle sıkıcıdır. Az sayıda kritik kaynak erken gelir. Kritik olmayan kaynaklar bekler. Üçüncü taraf script’ler geciktirilir, sınırlandırılır veya kaldırılır. Tarayıcı sayfayı boyayabilmeden önce yirmi önceliği idare etmeye zorlanmaz.
Yavaş bir sayfanın waterfall’u çoğu zaman gergindir: çok sayıda küçük dosya, çok sayıda origin ve çok sayıda geç keşif.
Daha küçük dosyalar hâlâ önemlidir — sadece eşit ölçüde değil
Bu, sıkıştırmaya veya optimizasyona karşı bir argüman değildir. Byte’ları optimize ederken koordinasyonu görmezden gelmeye karşı bir argümandır.
Daha küçük dosyalar en çok kaynak büyük, render-blocking veya ana içerik yolunun parçası olduğunda önemlidir. Örneğin:
- Hero görseli doğru boyutlandırılmalı ve encode edilmelidir.
- Render-blocking CSS yalın olmalıdır.
- İlk etkileşim için gereken JavaScript minimumda olmalıdır.
- Fontlar subset edilmeli, sıkıştırılmalı ve gerçekten kullanılan weight’lerle sınırlanmalıdır.
Fontlar yaygın bir örnektir. Ekipler çoğu zaman bir font dosyasının 24 KB mı 31 KB mı olduğuna takılırken altı weight, iki style ve birden fazla family gönderir. Daha iyi çözüm bir dosyadan 7 KB tıraşlamak değildir. Daha az font dosyası göndermektir. Tipografi performans çalışmanızın bir parçasıysa, web fontları çoğu sitede hâlâ en kolay kazanımlardan biridir.
Görseller de aynı deseni izler. AVIF veya WebP anlamlı byte tasarrufu sağlayabilir; ancak fold üstünde on dekoratif görsel göndermek hâlâ kötü bir plandır. Daha iyi formatlar seçin, evet; ama her görselin gerçekten istenmesi gerekip gerekmediğini de sorgulayın. Format kararları için AVIF ne zaman WebP’yi geçer, ne zaman geçmez rehberimiz bu istek sayısı çalışmasına yararlı bir eşlikçidir.
Çok sayıda küçük dosyanın gizli maliyetleri
Çok sayıda küçük istek, yalnızca toplam aktarılan byte’a bakarsanız görünmeyen sorunlar yaratma eğilimindedir.
1. Geç keşif
Tarayıcılar keşfetmedikleri şeyi isteyemez. Bir CSS dosyası bir font’a referans verebilir. Bir script başka bir script’i import edebilir. Bir component hydration’dan sonra JSON isteyebilir. Her bağımlılık zincirde bir adım daha yaratır.
Zincir ne kadar derinse, önemli iş o kadar geç başlar.
2. Header ek yükü
Her istek ve yanıt header içerir. Header compression özellikle HTTP/2 ve HTTP/3 üzerinde yardımcı olur, ancak ek yükü ortadan kaldırmaz. Cookie’ler bunu çok daha kötü hâle getirebilir. Siteniz her istekle büyük cookie’ler gönderiyorsa, küçük varlıklar pratikte daha az küçük olur.
Statik varlıkların çoğu zaman cookie’siz path veya domain’lerde yaşaması gerektiğinin ve cache header’larının ilgiyi hak ettiğinin nedenlerinden biri budur. Header’lar production’da tuhaf davranıyorsa, redirect ve HTTP header’larını debug etmek genellikle tahmin etmekten daha hızlıdır.
3. Main-thread kesintisi
Çok sayıda JavaScript chunk’ı tekrarlanan ayrıştırma ve çalıştırma işi yaratabilir. Her chunk küçük olsa bile tarayıcı kodu değerlendirmek için sürekli durabilir. Bu, Interaction to Next Paint’e zarar verebilir ve sayfanın titrek hissettirmesine neden olabilir.
Kullanıcı her dosyanın küçük olmasıyla ilgilenmez. Bir menüye dokunmanın 600 milisaniye sürmesiyle ilgilenir.
4. Cache karmaşıklığı
Varlıkları bölmek dikkatli yapıldığında caching’i iyileştirebilir. Stabil bir vendor bundle ve değişen bir app bundle iyi bir ayrım olabilir.
Ancak aşırı chunking geri tepebilir. Daha fazla dosya, daha fazla cache lookup, daha fazla revalidation fırsatı, daha fazla version koordinasyonu ve değişmesi gerekmeyen kaynakları yanlışlıkla invalidate etmenin daha fazla yolu demektir.
Bundling geri döndü, ama muhakemeyle
Web performansının ilk dönemi bundling’i severdi çünkü tarayıcıların katı bağlantı sınırları vardı. Sonra HTTP/2 geldi ve birçok ekip agresif code splitting’e sert biçimde yöneldi. Bunun bir kısmı yararlıydı. Bir kısmı batıl inanca dönüştü.
Mantıklı orta yol route-aware bundling’dir.
Tipik bir marketing sitesi veya content sitesi için:
- İlk render için gereken CSS’i inline edin veya yalnızca onu yükleyin.
- Global JavaScript’i küçük tutun.
- Küçük modülleri ayrı network isteklerine bölmekten kaçının.
- Hemen gerekmeyen etkileşimli özellikleri geciktirin.
- Maliyetini haklı çıkarmayan üçüncü taraf script’leri kaldırın.
Bir application için:
- Her component’e göre değil, route veya büyük feature’a göre bölün.
- Paylaşılan bağımlılıkları stabil ve cache’lenebilir tutun.
- Yalnızca yakında kesinlikle gerekecek kaynakları preload edin.
- Public sayfalarda admin, dashboard, editor veya experiment kodu yüklemekten kaçının.
- Yalnızca bundle boyutunu değil, etkileşim maliyetini ölçün.
Bundling otomatik olarak iyi değildir. Code splitting otomatik olarak iyi değildir. Yararlı soru şudur: Bu bölme, tarayıcının bir sonraki anlamlı kullanıcı deneyimini daha erken sunmasına yardımcı oluyor mu?
Üçüncü taraf istekler ekstra şüpheyi hak eder
Birinci taraf istekler en azından sizin kontrolünüzdedir. Üçüncü taraf istekler ise çoğu zaman göründüklerinden daha yavaş, daha az öngörülebilir ve daha pahalıdır.
Tek bir tag manager analytics, ads, heatmap’ler, chat widget’ları, A/B testing, consent araçları ve personalization script’lerini tetikleyebilir. Her vendor daha fazla istek getirebilir. Bazıları erken çalışır. Bazıları main thread’i engeller. Bazıları release süreciniz olmadan değişir.
En iyi üçüncü taraf optimizasyonu silmektir. İkinci en iyisi geciktirmektir.
Üçüncü taraf bir script eklemeden önce şunları sorun:
- Kullanıcı sayfayı görmeden önce bunun yüklenmesi gerekiyor mu?
- Her sayfada yüklenmesi gerekiyor mu?
- Consent, etkileşim veya idle time sonrasında yüklenebilir mi?
- İçeride bunun sahibi kim?
- Performans maliyetine değdiğini hangi metrik kanıtlıyor?
Performansın governance’a dönüştüğü yer burasıdır. Birinin hayır diyebilmesine izin verilmelidir.
Pratik bir istek azaltma kontrol listesi
En önemli sayfalardan başlayın: homepage, pricing page, product page, checkout, signup veya en iyi landing page’ler. Sonra waterfall üzerinde ilerleyin.
Kaldırın
- Kullanılmayan JavaScript ve CSS’i silin.
- Eski experiment’leri, terk edilmiş pixel’leri ve duplicate analytics’i kaldırın.
- Kullanılmayan font weight’lerini ve icon library’lerini çıkarın.
- Uygun olduğunda dekoratif görselleri CSS ile değiştirin.
Düşünerek birleştirin
- Her zaman birlikte yüklenen küçük JavaScript modüllerini bundle edin.
- Aynı render path’i engelleyen küçük CSS dosyalarını birleştirin.
- Bakım yapılabilirliğe zarar vermeden istekleri azaltıyorsa tekrar eden ikonlar için SVG sprite’ları veya inline SVG kullanın.
Erteleyin
- Fold altındaki görselleri lazy-load edin.
- Kritik olmayan script’leri first paint veya kullanıcı etkileşiminden sonraya geciktirin.
- Yorumları, embed’leri, haritaları, chat’i ve video player’ları yalnızca gerektiğinde yükleyin.
Doğru cache’leyin
- Version’lanmış statik varlıklar için uzun ömürlü caching kullanın.
- Nadiren değişen dosyalar için gereksiz revalidation’dan kaçının.
- HTML’i güncel tutun, ama hashed varlıkların cache’te kalmasına izin verin.
Yeniden ölçün
Her değişiklikten sonra waterfall’u tekrar kontrol edin. Hedef mükemmel bir skor değildir. Hedef daha az kritik istek, daha erken yararlı render ve daha az main-thread kesintisidir.
İyi olan nasıl görünür
Sağlıklı bir sayfanın mümkün olan en az isteğe sahip olması gerekmez. Küçük ve bilinçli bir critical path’e sahiptir.
Tarayıcı HTML’i, temel CSS’i, varsa ana içerik görselini, belki navigation veya above-the-fold etkileşim için gereken küçük bir script’i ve metni okunabilir yapmak için gereken minimum font setini alır. Diğer her şey sırasını bekler.
Yalnızca optimize edilmiş bir sayfa ile hızlı hissettiren bir sayfa arasındaki fark budur.
Dosyaları küçültmek hâlâ değerlidir. Ama site zaten makul ölçüde sıkıştırılmışsa, bir sonraki performans kazanımı genellikle bir bundle’dan bir 2 KB daha tasarruf etmek değildir. Bir eksik blocking request, bir eksik font dosyası, bir eksik üçüncü taraf script, bir eksik bağımlılık zinciridir.
Daha az istek tarayıcının işini basitleştirir. Basit olan, kabul etmek istediğimizden daha sık hızlıdır.