Lazy Loading Angular — Angular

Lazy loading memuat module/komponen hanya saat dibutuhkan — memperkecil bundle awal. Lazy Route export const routes: Routes = [ { path: "admin"…

Lazy loading membuat kode sebuah halaman baru diunduh saat halaman itu pertama kali dibuka, bukan saat aplikasi start. Hasilnya: unduhan awal lebih kecil, dan halaman pertama tampil lebih cepat. Bayangin Bang Jali membuka gerobak tiap sore. Dia gak membawa semua bahan dari gudang; yang dibawa cuma bahan menu yang hampir pasti dipesan. Bahan martabak spesial baru diambil dari gudang saat ada yang memesannya, dan sekali sudah diambil, bahannya tetap di gerobak. Gerobak itu bundle awal, gudang itu file chunk di server, dan pesanan pertama itu kunjungan pertama ke rutenya.

🧪 Tebakan cepat: Rute Pesanan dibuat lazy. Kamu buka /menu, klik Pesanan (chunk-nya diunduh), balik ke Menu, lalu klik Pesanan sekali lagi. Chunk Pesanan diunduh berapa kali? Jawabannya ada di bagian "Diunduh sekali, saat pertama dibuka".

Dari component ke loadComponent

Daftar rute dari pelajaran Routing memakai component, jadi semua halaman ikut di bundle awal:

// src/app/app.routes.ts, versi eager
import { Routes } from '@angular/router'
import { MenuPage } from './menu/menu'
import { PesananPage } from './pesanan/pesanan'
import { ADMIN_ROUTES } from './admin/admin.routes'

export const routes: Routes = [
  { path: '', redirectTo: 'menu', pathMatch: 'full' },
  { path: 'menu', component: MenuPage },
  { path: 'pesanan', component: PesananPage },
  { path: 'admin', children: ADMIN_ROUTES },
]

Versi lazy-nya:

// src/app/app.routes.ts, versi lazy
import { Routes } from '@angular/router'
import { MenuPage } from './menu/menu'

export const routes: Routes = [
  { path: '', redirectTo: 'menu', pathMatch: 'full' },
  { path: 'menu', component: MenuPage },
  {
    path: 'pesanan',
    loadComponent: () => import('./pesanan/pesanan').then((m) => m.PesananPage),
  },
  {
    path: 'admin',
    loadChildren: () => import('./admin/admin.routes').then((m) => m.ADMIN_ROUTES),
  },
]

Apa yang dihasilkan ng build

Ini output ng build (Angular 20) untuk aplikasi contoh Martabak Bang Jali, persis seperti dicetak. Versi eager:

Initial chunk files   | Names         |  Raw size | Estimated transfer size
main-DJOC73FW.js      | main          | 239.57 kB |                63.52 kB
polyfills-5CFQRCPP.js | polyfills     |  34.59 kB |                11.33 kB

                      | Initial total | 274.16 kB |                74.86 kB

Versi lazy:

Initial chunk files   | Names         |  Raw size | Estimated transfer size
chunk-KN7FB5LX.js     | -             | 120.30 kB |                36.09 kB
main-FV3QO47T.js      | main          |  83.95 kB |                21.56 kB
polyfills-5CFQRCPP.js | polyfills     |  34.59 kB |                11.33 kB
chunk-3CKZ4X2Z.js     | -             |   5.63 kB |                 1.61 kB

                      | Initial total | 244.47 kB |                70.59 kB

Lazy chunk files      | Names         |  Raw size | Estimated transfer size
chunk-M4JL3FZ7.js     | pesanan       |  31.18 kB |                 7.12 kB
chunk-KQMY4PR3.js     | admin-routes  | 614 bytes |               614 bytes

Diunduh sekali, saat pertama dibuka

Hasil build tadi dibuka di Chrome, dan ini file JavaScript yang diminta browser di tiap langkah:

Langkah File yang diminta
Buka / (dialihkan ke /menu) chunk-3CKZ4X2Z.js, chunk-KN7FB5LX.js, polyfills-5CFQRCPP.js, main-FV3QO47T.js
Klik Pesanan chunk-M4JL3FZ7.js
Klik Menu gak ada
Klik Pesanan lagi gak ada
Klik Admin chunk-KQMY4PR3.js

Chunk Pesanan diunduh sekali, saat /pesanan pertama dibuka. Itu jawaban tebakannya. Router menyimpan komponen yang sudah dimuat, jadi loadComponent gak dipanggil lagi. Selama chunk-nya masih diunduh, halaman lama tetap tampil; outlet baru berganti setelah chunk-nya sampai.

loadChildren: satu chunk untuk sekelompok rute

// src/app/admin/admin.routes.ts
import { Routes } from '@angular/router'
import { AdminStok } from './admin-stok'
import { AdminLaporan } from './admin-laporan'

export const ADMIN_ROUTES: Routes = [
  { path: '', component: AdminStok },
  { path: 'laporan', component: AdminLaporan },
]

loadChildren memuat daftar rute, bukan satu komponen. /admin dan /admin/laporan sama-sama ada di chunk-KQMY4PR3.js, jadi pindah ke laporan gak perlu unduhan lagi.

Di Playground: gak ada bundler

Playground menjalankan satu file langsung di browser, tanpa bundler dan tanpa file lain. Rute dengan import('./pesanan/pesanan') di sana gagal saat Pesanan diklik, halamannya tetap di Menu, dan console menulis:

ERROR TypeError: Failed to resolve module specifier './pesanan/pesanan'

Karena itu, kode Lab di Playground mengganti import() dengan Promise yang mengembalikan class dari file yang sama 600 ms kemudian. Router memperlakukannya sama: console menulis mengunduh chunk pesanan sekali, di kunjungan pertama saja.

Latihan juga berjalan di editor seperti ini. import() di dalam fungsi loadComponent gak pernah dipanggil, karena gak ada yang membuka /admin, jadi gak ada error. Import biasa di atas file malah langsung gagal saat kode dijalankan: Failed to resolve module specifier "./admin/admin.component". Invalid relative url or base scheme isn't hierarchical. Bedanya kelihatan jelas: import biasa jalan saat start, import() menunggu dipanggil.

📌 Preloading dan @defer

⚠️ Jebakan yang sering ketemu

TL;DR: Lazy loading menunda unduhan kode sebuah halaman sampai rutenya pertama dibuka. loadComponent: () => import('./pesanan/pesanan').then((m) => m.PesananPage) memakai dynamic import, import(), yang baru jalan saat router memanggil fungsinya; loadChildren melakukan hal yang sama untuk sekelompok rute. ng build memisahkannya ke chunk sendiri (di contoh, bundle awal turun dari 274,16 kB jadi 244,47 kB), chunk itu diunduh sekali saat kunjungan pertama, dan halaman lama tetap tampil sampai chunk-nya sampai. Jangan meng-import komponen lazy secara biasa di file mana pun, isi loadComponent dengan fungsi, dan jangan lupa .then(...).

Yang akan kamu pelajari