Skip to content

Bölüm 11 / 13

Performans, @defer ve SSR

Deferrable views, hydration, profil alma.

11 dakikalık okuma

Performans parçaları hattın içine dağılmıştı: bütçeler (1. ders), track (3. ders), lazy rota (7. ders), OnPush ve signals (9. ders). Bu ders parçaları bir teşhis sürecine bağlar: neyi, neyle ölçersin; hangi belirtiye hangi araç? Üstüne iki modern katman ekler: şablon içi tembellik (@defer) ve sunucu tarafı render (SSR + hydration) — "ilk boya neden geç?" sorusunun bugünkü cevapları. Sonda kısa bir ekosistem turu var: Material/CDK, i18n, animasyonlar.

01Ne öğreneceksin

  • Tembellik katmanları karar tablosu: lazy rota · @defer · sanal kaydırma
  • @defer ayrıntısı: tetikleyiciler, @placeholder/@loading/@error, prefetch
  • Ölçüm disiplini: Angular DevTools profiler, paket analizi, Core Web Vitals
  • SSR + hydration: neden, nasıl; window tuzağı ve afterNextRender; çift istek ve TransferState
  • Ekosistem haritası: Material vs CDK, i18n, animasyonlar — ne zaman hangisi

02Tembellik katmanları: doğru araca doğru belirti

Frontend performansının ana ilkesi tektir: kullanıcının şu an görmediği şeyin bedelini şimdi ödeme. Angular'da bu ilkenin üç katmanı vardır ve karıştırılması sık bir mimari hatadır:

KatmanAraçBelirti / doğru iş
RotaloadComponent/loadChildren (7. ders)İlk paket büyük; kullanıcının belki hiç girmeyeceği bölgeler var (yönetim, raporlar)
Şablon bölgesi@defer (bu ders)Sayfa açık ama içindeki bir parça pahalı: sayfa altındaki grafik, yorumlar, ağır bir üçüncü parti bileşen
Liste satırıSanal kaydırma (CDK virtual-scroll)Binlerce satır: DOM'da yalnız görünen pencere tutulur; track/OnPush artık yetmiyorsa

Sıralama da budur: önce rota sınırları, sonra sayfa içi bölgeler, en son satır düzeyi. Tersinden başlamak — daha rota bölünmemişken sanal kaydırma kurmak — karmaşıklığı öne, kazancı arkaya koyar.

03@defer: şablon içi tembellik

Lazy rota, sınırı URL'den çizer; @defer sınırı şablonun içinden çizer — bloğun içindeki bileşenler ayrı parçaya (chunk) alınır ve tetikleyici gerçekleşene dek ne kodu iner ne render edilir:

@defer (on viewport; prefetch on idle) {
  <app-maas-grafigi [veriler]="veriler()" />
} @placeholder {
  <div class="grafik-iskelet" aria-hidden="true"></div>
} @loading (minimum 300ms) {
  <p>Grafik yükleniyor…</p>
} @error {
  <p role="alert">Grafik yüklenemedi.</p>
}

Bu altı satırda dersin yarısı var; parçalayalım:

TetikleyiciNe zaman yüklerTipik iş
on viewportPlaceholder görünür alana girinceSayfa altı bölgeler: grafik, yorumlar
on idle (varsayılan)Tarayıcı boş kalınca"Az sonra lazım olacak" bölgeler
on interaction / on hoverPlaceholder'a tıklanınca / gelininceAçılır panel, sekme içeriği
on timer(2s)Süre doluncaNadir; kampanya/duyuru bölgesi
when kosul()İfade true oluncaKoşula bağlı ağır içerik — @if'ten farkı: kod da tembel
  • prefetch ayrı karardır: prefetch on idle — kodu boşta indir ama render'ı tetikleyiciye bırak; viewport'a gelindiğinde bekleme sıfıra iner. "İndirme" ile "çizme"nin ayrışması, bu bloğun en zarif fikridir.
  • @placeholder yer tutar: boyutu gerçek içeriğe yakın bir iskelet ver — yoksa içerik gelince sayfa zıplar (8. hattın CLS/düzen kayması bilgisi). @loading (minimum 300ms)'un derdi de titremedir: çok hızlı biten yüklemede göstergenin bir anlığına parlayıp sönmesini engeller.
  • Ayrı parça şartı: @defer'ın kod bölmesi, içindeki bileşen yalnız o blokta kullanılıyorsa çalışır — bileşeni aynı dosyada başka yerde de kullanıyorsan zaten ana pakettedir, blok yalnız render'ı geciktirir. "Defer koydum, paket küçülmedi" şaşkınlığının cevabı budur.

04Ölçüm disiplini: tahmin değil, profil

Bu hattın her performans kuralı bir belirtiyle geldi; şimdi belirtiyi ölçme araçlarını sırala. Kural, 2. hattan beri aynı: ölçmeden optimize etme — tahminle yapılan optimizasyon, çoğu zaman yanlış yeri hızlandırır.

  • İlk yük yavaşsa → paket analizi: üretim derlemesi çıktısındaki parça boyutlarına bak (ng build tablosu; ayrıntı için source map görselleştiricileri). Soru hep aynıdır: "bu paket neden bu boyda — içinde ne var?" Çoğu şişkinliğin cevabı, ana pakete sızmış bir ağır kütüphanedir; çözüm lazy sınırıdır. 1. dersin bütçeleri bu kontrolü CI'a bağlar: eşik aşılırsa derleme kırılır — gerileme fark edilmeden yayılamaz.
  • Etkileşim yavaşsa → Angular DevTools profiler: tarayıcı eklentisi; her değişiklik algılama turunun süresini ve hangi bileşenin ne kadar yediğini gösterir. "Yazarken arayüz takılıyor" şikâyetinde ilk bakılacak yer: turda binlerce kez çalışan bir şablon metot çağrısı ya da saf olmayan pipe (10. ders) burada yakalanır; OnPush/signals kazancı da burada kanıtlanır.
  • Kullanıcı gerçeğine bak → Core Web Vitals: 8. hattın DevTools dersindeki Lighthouse/Performance panelleri Angular'da da geçerlidir; LCP (en büyük içerik boyası) SSR kararının, CLS (düzen kayması) placeholder disiplininin ölçüsüdür.

05SSR ve hydration: ilk boyayı öne almak

SPA'nın yapısal zaafını 1. dersten biliyorsun: tarayıcı önce boş bir index.html + JS paketi alır; kullanıcı, paket inip çalışana dek beyaz ekrana bakar. İçerik odaklı ve SEO'nun önemli olduğu sayfalarda bu kabul edilmez — çözüm, ilk HTML'i sunucuda üretmektir (ng new --ssr). Akış üç perdedir:

  1. Sunucu render eder: Node üzerinde uygulama çalıştırılır, rota çizilir, dolu HTML döner. Kullanıcı içeriği hemen görür; arama motoru dolu sayfa okur.
  2. İstemci paketi iner ve Angular, sunucunun ürettiği DOM'u hydrate eder: sıfırdan çizmek yerine var olan DOM'u sahiplenir, dinleyicileri bağlar. (Hydration olmadan SSR, sayfayı ikinci kez baştan çizerdi — ekran titrer, kazanç yarıya inerdi.) Hydration tamamlanana dek biriken tıklamalar da kaydedilip sırasıyla işlenir — event replay.
  3. Uygulama SPA olarak devam eder: sonraki geçişler yine router'ındır (7. ders) — SSR yalnız ilk yükleme hikâyesidir.

İki üretim tuzağını bilmek, "SSR açtım, patladı" gününde seni kurtarır:

  • window sunucuda yoktur: Node'da window, document, localStorage tanımsızdır — bunlara sınıf gövdesinden/kurucudan dokunan her kod sunucu render'ını düşürür. Bu hattın temalarında bu yüzden hep tarayıcıya dokunan işler afterNextRender'a kondu (10. ders): o blok yalnız tarayıcıda çalışır. İnce ayrım gerekiyorsa platform kontrolü vardır (isPlatformBrowser); ama ilk refleks "DOM işi render sonrasına" olmalı.
  • Çift istek tuzağı ve TransferState: sunucu render ederken API'den veriyi çekti; istemci hydrate olurken aynı isteği bir daha atarsa emek boşa gider (ve ekran bir anlığına titreyebilir). Angular'ın HttpClient'ı SSR'da bunu kendiliğinden çözer: sunucuda çekilen cevaplar HTML'in içine gömülür (TransferState), istemci ilk hydration'da isteği ağa çıkmadan oradan okur. Mekanizmayı bilmek önemli çünkü kapsamı sınırlı: yalnız GET ve ilk yükleme — "neden bazı istekler hiç atılmıyor?" sorusunun cevabı da budur.

Karar ölçüsü: giriş yapılmış, içerik herkese kapalı bir yönetim paneli SSR'dan neredeyse hiç kazanmaz; pazarlama sayfaları, ilan listeleri gibi ilk boyanın ve SEO'nun para ettiği yüzeyler kazanır. SSR bir sunucu işletmek demektir (12. hat Node bilgisi) — bedava değildir, gerekçesiyle açılır.

06Ekosistem turu: Material, CDK, i18n, animasyon

ParçaNedirKarar notu
Angular MaterialHazır bileşen kütüphanesi (tablo, dialog, form alanları) — Material Design görünümlüİç araçlar/panolar için hız; kuruma özel tasarımda görünümüyle boğuşmak pahalı olabilir
CDK (Component Dev Kit)Görünümsüz davranış primitifleri: overlay, drag-drop, virtual scroll, a11y araçlarıAsıl mücevher budur: kendi tasarım sistemini kuran ekip Material'ı değil CDK'yı alır
i18nÇeviri: resmi @angular/localize derleme zamanı (dil başına ayrı çıktı) çalışır; çalışma anı kütüphaneleri de yaygın3. dersin LOCALE_ID uyarısı buranın kapısıydı: tarih/para biçimleri de yerelle döner
AnimasyonlarGeçiş/durum animasyonları; modern yönelim CSS'e yaklaşıyorÖnce 8. hattın CSS geçişleri; koşullu girip çıkan elementlerin animasyonu gerekince çerçeve API'sine bak

Bu tablo bilinç haritasıdır, müfredat değil: hangi sorunun hangi kutuda çözüldüğünü bil; derinleşme işe girince ihtiyaçla gelir.

07Üretim pratiği: performans bir süreçtir

  • Sıra: ölç → en pahalı tekil sebebi bul → düzelt → yeniden ölç. Tek seferde beş optimizasyon yapan, hangisinin işe yaradığını asla öğrenemez (2. hattın deney disiplini).
  • Bütçeyi gerilemeden önce kur: angular.json bütçeleri + CI'da üretim derlemesi — paket şişmesi kod incelemesinde değil, derleme kırılınca yakalanır.
  • Placeholder'sız defer, gösterge titreten loading bırakma: @defer her zaman boyutlu bir @placeholder ve minimum'lu bir @loading ile gelir — performans kazancı, düzen kaymasıyla geri ödenmesin.
  • SSR'ı sonradan açmak pahalıdır: baştan SSR düşünülen kodda "DOM'a yalnız afterNextRender'da dokun" kuralı doğal alışkanlıktır; sonradan açılan projede yüzlerce window erişimi tek tek ayıklanır. Yeni projede kararı 1. günde ver.

08Mini alıştırma — Defer ve profil deneyleri

Yaklaşık 20 dakika · kurulum yok — StackBlitz'te Angular şablonu; Angular DevTools için tarayıcı eklentisi (varsa) artı puan

  1. Sayfanın altına "ağır" bir bileşen koy (kurucusunda 500 satırlık sahte tablo üreten). Önce düz koy ve sayfanın açılışını gözle; sonra @defer (on viewport) + boyutlu @placeholder ile sar — Network/Elements sekmelerinde bileşenin ancak kaydırınca geldiğini gör.
  2. prefetch on idle ekle; Network'te parçanın erken indiğini ama render'ın hâlâ viewport'u beklediğini ayırt et.
  3. @loading (minimum 300ms) ve @error bloklarını ekle; DevTools'ta ağı yavaşlatıp üç durumu da gözle.
  4. Şablon metodu tuzağını ölç: listende {{ hesapla(b) }} çağrısı olan bir sütun aç, içine console.count koy; sayfada gezinip sayacın patlamasını izle (9. dersin bilgisiyle sebebini açıkla), sonra computed/pipe'a taşı ve sayacın durulduğunu gör.
  5. Zıplama deneyi: @placeholder'ı bilerek boş bırak ve içerik gelince sayfanın zıpladığını gör; iskelet verip düzelt — CLS'in ne olduğunu bir cümleyle yaz.
  6. Karar alıştırması (kâğıt üstünde): "yönetim ekranları", "sayfa altındaki maaş grafiği", "10.000 satırlık log tablosu" — her biri için 02. bölümdeki tablodan doğru katmanı seç ve bir cümleyle gerekçele.

09Kendini sına

Angular'da tembellik katmanlarını say; her birinin doğru belirtisi nedir?

Üç katman var ve sıralama önemli. (1) Rota düzeyi — loadComponent/loadChildren: ilk paket büyükse ve kullanıcının belki hiç girmeyeceği bölgeler (yönetim, raporlar) ana pakete gömülüyse; kod, rotaya ilk girişte iner. (2) Şablon bölgesi — @defer: sayfa açık ama içindeki bir parça pahalıysa — sayfa altındaki grafik, yorumlar, ağır üçüncü parti bileşen; bloğun kodu ayrı parçaya alınır, tetikleyiciye dek ne iner ne çizilir. (3) Satır düzeyi — sanal kaydırma (CDK): binlerce satırlık listede DOM'da yalnız görünen pencereyi tutmak; track ve OnPush artık yetmediğinde. Doğru sıra da budur: önce rota sınırları, sonra sayfa içi bölgeler, en son satır düzeyi — tersinden başlamak, karmaşıklığı öne kazancı arkaya koyar. Hepsinin ortak ilkesi: kullanıcının şu an görmediği şeyin bedelini şimdi ödeme; ve hepsinin kanıtı ölçümdür — paket tablosu, Network'te inen parça, profiler.

@defer bloğunu anlat: tetikleyiciler, alt bloklar, prefetch. Lazy rotadan farkı nedir; "defer koydum, paket küçülmedi" nasıl olur?

@defer, şablonun bir bölgesini hem kod hem render olarak erteler: içindeki bileşenler ayrı parçaya derlenir ve tetikleyici gerçekleşene dek yüklenmez. Tetikleyiciler: on viewport (görünür alana girince — sayfa altı bölgelerin standardı), on idle (varsayılan; tarayıcı boşta), on interaction/hover, on timer, when kosul. Alt bloklar durum tasarımıdır: @placeholder yüklemeden önce yer tutar — boyutlu bir iskelet verilmezse içerik gelince sayfa zıplar (CLS); @loading (minimum 300ms) gösterge titremesini engeller; @error yükleme başarısızlığını çizer. prefetch indirme ile çizmeyi ayırır: prefetch on idle; on viewport — kod boşta iner, render görünürlüğü bekler; kullanıcı geldiğinde bekleme sıfırdır. Lazy rotadan farkı sınırın yeri: rota URL'den böler, defer şablon içinden. Paketin küçülmemesinin klasik sebebi: defer içindeki bileşen başka yerde de (eager) kullanılıyordur — o zaman kod zaten ana pakettedir, defer yalnız render'ı geciktirir; kod bölmesi, bileşenin yalnız defer bloklarında kullanılmasını ister.

SSR ve hydration nasıl çalışır? window tuzağını ve TransferState'i anlat; SSR'ı hangi projede açarsın?

SSR'da ilk HTML sunucuda üretilir: Node üzerinde uygulama çalıştırılır, rota render edilir, dolu HTML döner — kullanıcı beyaz ekran yerine içeriği hemen görür, arama motoru dolu sayfa okur. İstemci paketi inince Angular DOM'u sıfırdan çizmez, hydrate eder: sunucunun ürettiği DOM'u sahiplenir ve dinleyicileri bağlar (hydration'sız SSR sayfayı ikinci kez çizer, ekran titrer); bu sırada biriken tıklamalar event replay ile işlenir. Sonrası normal SPA'dır — SSR yalnız ilk yükleme hikâyesidir. Birinci tuzak: Node'da window, document, localStorage yoktur — kurucudan bunlara dokunan kod sunucuda çöker; DOM'a dokunan işler afterNextRender'a (yalnız tarayıcıda çalışır) alınır, gerekirse isPlatformBrowser ile dallanılır. İkinci tuzak çift istektir: sunucu veriyi çekti, istemci hydration'da aynı isteği tekrar atarsa emek boşa gider; HttpClient sunucuda çekilen GET cevaplarını HTML'e gömer (TransferState), istemci ilk yüklemede ağa çıkmadan oradan okur. Karar: SEO ve ilk boyanın para ettiği yüzeylerde (ilan listeleri, içerik sayfaları) açılır; girişli yönetim paneli neredeyse hiç kazanmaz — SSR bir sunucu işletmektir, gerekçesiz açılmaz.

Performans şikâyeti geldi: "uygulama yavaş." Teşhis sürecini ve araçlarını anlat.

İlk iş şikâyeti ayrıştırmaktır — "yavaş"ın en az üç ayrı hastalığı vardır ve araçları farklıdır. (1) İlk açılış yavaşsa paket sorunudur: üretim derlemesinin parça tablosuna ve source map analizine bakarım — "bu paket neden bu boyda, içinde ne var?"; çözüm tipik olarak lazy sınırı ya da ana pakete sızmış ağır bir bağımlılığı defer/lazy arkasına almaktır; angular.json bütçeleri bu sınıfı CI'da kilitler. (2) Etkileşim yavaşsa (yazarken takılma, tıklamada gecikme) değişiklik algılama sorunudur: Angular DevTools profiler ile turların süresini ve pahalı bileşeni bulurum; klasik failler şablonda metot çağrısı, saf olmayan pipe, track'siz büyük liste, OnPush'suz geniş ağaç — düzeltmenin kanıtı yine profiler'dır. (3) Kullanıcı algısı kötüyse Core Web Vitals'a bakarım: LCP kötüyse SSR/defer/görsel önceliklendirme; CLS kötüyse boyutsuz placeholder ve zıplayan düzen. Süreç hep aynıdır: ölç → tek sebep bul → düzelt → yeniden ölç; tek seferde beş optimizasyon yapan hangisinin işe yaradığını öğrenemez. Kıdem cümlesi: optimizasyon kanıtla başlar, kanıtla biter.

10Sınav köşesi

Yazılıda nasıl sorulur

(1) Angular'da tembel yükleme hangi düzeylerde yapılır? (2) @defer bloğu nedir; hangi tetikleyicileri vardır? (3) @placeholder ve @loading blokları neden önemlidir? (4) prefetch ne sağlar? (5) SSR nedir; hangi sorunu çözer? (6) Hydration nedir; olmasa ne olurdu? (7) SSR'da window'a erişim neden hata verir; çözümü nedir? (8) TransferState hangi tuzağı kapatır? (9) Angular DevTools profiler ne zaman kullanılır? (10) Angular Material ile CDK arasındaki fark nedir?

Sözlüde nasıl anlatılır — 2 dakikalık taslak

Düzen: (1) İlke — "Kullanıcının görmediği şeyin bedeli şimdi ödenmez: rota düzeyinde lazy, şablon bölgesinde @defer, satır düzeyinde sanal kaydırma — bu sırayla." (2) @defer — "on viewport + prefetch on idle kalıbım: kod boşta iner, render görünürlüğü bekler; boyutlu placeholder CLS'i, minimum'lu loading titremeyi önler." (3) SSR — "İlk HTML sunucuda, sonra hydration + event replay; DOM işi afterNextRender'da — window sunucuda yok; TransferState çift isteği keser; SEO'lu yüzeyde açarım, girişli panelde açmam." (4) Ölçüm — "Üç şikâyet üç araç: ilk yük → paket analizi + bütçe; etkileşim → DevTools profiler; algı → Core Web Vitals." (5) Kıdem vuruşu — "Ölç-düzelt-yeniden ölç döngüsü dışında optimizasyon yapmam; bütçeyi CI'a koyarım, gerileme derlemede kırılır."