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 @deferayrıntısı: tetikleyiciler,@placeholder/@loading/@error,prefetch- Ölçüm disiplini: Angular DevTools profiler, paket analizi, Core Web Vitals
- SSR + hydration: neden, nasıl;
windowtuzağı veafterNextRender; çift istek veTransferState - 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:
| Katman | Araç | Belirti / doğru iş |
|---|---|---|
| Rota | loadComponent/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:
| Tetikleyici | Ne zaman yükler | Tipik iş |
|---|---|---|
on viewport | Placeholder görünür alana girince | Sayfa altı bölgeler: grafik, yorumlar |
on idle (varsayılan) | Tarayıcı boş kalınca | "Az sonra lazım olacak" bölgeler |
on interaction / on hover | Placeholder'a tıklanınca / gelinince | Açılır panel, sekme içeriği |
on timer(2s) | Süre dolunca | Nadir; kampanya/duyuru bölgesi |
when kosul() | İfade true olunca | Koşula bağlı ağır içerik — @if'ten farkı: kod da tembel |
prefetchayrı 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.@placeholderyer 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 buildtablosu; 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:
- 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.
- İ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.
- 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:
windowsunucuda yoktur: Node'dawindow,document,localStoragetanı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şlerafterNextRender'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ça | Nedir | Karar notu |
|---|---|---|
| Angular Material | Hazı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ın | 3. dersin LOCALE_ID uyarısı buranın kapısıydı: tarih/para biçimleri de yerelle döner |
| Animasyonlar | Geç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.jsonbü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:
@deferher zaman boyutlu bir@placeholderveminimum'lu bir@loadingile 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üzlercewindoweriş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
- 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@placeholderile sar — Network/Elements sekmelerinde bileşenin ancak kaydırınca geldiğini gör. prefetch on idleekle; Network'te parçanın erken indiğini ama render'ın hâlâ viewport'u beklediğini ayırt et.@loading (minimum 300ms)ve@errorbloklarını ekle; DevTools'ta ağı yavaşlatıp üç durumu da gözle.- Şablon metodu tuzağını ölç: listende
{{ hesapla(b) }}çağrısı olan bir sütun aç, içineconsole.countkoy; sayfada gezinip sayacın patlamasını izle (9. dersin bilgisiyle sebebini açıkla), sonracomputed/pipe'a taşı ve sayacın durulduğunu gör. - 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. - 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."
