Dynamic import(): Muat Kode Saat Dibutuhkan
Import biasa memuat semua modul di awal, sebelum aplikasi jalan. Tapi bagaimana kalau ada fitur berat yang jarang dipakai, seperti editor PDF atau peta interaktif? Memuatnya di awal membuat semua user menunggu. Dynami...
Dynamic import(): Muat Kode Saat Dibutuhkan
Import biasa memuat semua modul di awal, sebelum aplikasi jalan. Tapi bagaimana kalau ada fitur berat yang jarang dipakai, seperti editor PDF atau peta interaktif? Memuatnya di awal membuat semua user menunggu. Dynamic import() memuat modul hanya saat benar-benar dibutuhkan.
Kenapa dynamic import ada
Ukuran JavaScript yang diunduh saat halaman dibuka berbanding lurus dengan waktu loading. Aplikasi besar bisa punya ratusan modul, tapi user mungkin hanya memakai 20 persennya dalam satu kunjungan. Dynamic import memecah bundle menjadi potongan-potongan (code splitting): kode inti dimuat dulu supaya halaman cepat tampil, sisanya diunduh on-demand saat user mengklik fitur tertentu. Ini teknik standar di balik "loading" yang muncul sesaat saat kamu buka halaman admin atau fitur premium.
Sintaks dasar
import() terlihat seperti fungsi, tapi sebenarnya sintaks khusus. Ia mengembalikan promise yang resolve menjadi namespace modul:
document.getElementById("btn-laporan").addEventListener("click", async () => {
// modul berat baru diunduh SAAT tombol diklik
const modulLaporan = await import("./laporan.js");
modulLaporan.tampilkanLaporan();
});Sebelum tombol diklik, laporan.js tidak diunduh sama sekali. Setelah diklik, browser mengambilnya, lalu fungsinya dijalankan. Klik kedua tidak mengunduh ulang karena modul di-cache.
Contoh realistis: fitur cetak struk yang jarang dipakai
Kasir warung dipakai ratusan kali sehari, tapi fitur cetak laporan bulanan mungkin dibuka sekali sebulan. Jangan bebankan semua kasir dengan kode laporan:
// laporan.js - modul berat (misalnya pakai library PDF 500KB)
export function cetakLaporanBulanan(transaksi) {
const total = transaksi.reduce((a, t) => a + t.nominal, 0);
return `Laporan: ${transaksi.length} transaksi, total Rp${total}`;
}// app.js - kode inti kasir, tetap ringan
async function bukaLaporan() {
tampilkanSpinner(true);
try {
const { cetakLaporanBulanan } = await import("./laporan.js");
const hasil = cetakLaporanBulanan(ambilTransaksiBulanIni());
tampilkanHasil(hasil);
} catch (error) {
tampilkanPesan("Gagal memuat fitur laporan. Periksa koneksi.");
} finally {
tampilkanSpinner(false);
}
}Pola ini penting: selalu bungkus dynamic import dengan try/catch dan tampilkan loading state, karena pengunduhan bisa gagal (user offline) dan butuh waktu.
Preload: strategi tingkat lanjut
Kadang kamu tahu user "hampir pasti" butuh modul itu, misalnya setelah login selesai. Kamu bisa mulai mengunduhnya di background selagi user membaca halaman:
// mulai unduh di background, jangan await
const janjiLaporan = import("./laporan.js");
// ... user mengisi form, beberapa detik berlalu ...
// saat dibutuhkan, kemungkinan sudah selesai diunduh
document.getElementById("btn-laporan").addEventListener("click", async () => {
const { cetakLaporanBulanan } = await janjiLaporan;
cetakLaporanBulanan(ambilTransaksiBulanIni());
});Karena import() mengembalikan promise biasa, kamu bisa menyimpannya dan await nanti. Kalau unduhannya sudah selesai, await-nya resolve seketika.
Jebakan umum
- Path harus bisa dianalisis bundler.
import("./laporan.js")dengan string literal bisa di-split oleh Vite/Webpack.import(namaFile)dengan variabel sembarang tidak bisa, bundler tidak tahu file apa saja yang mungkin diminta. - Jangan dynamic import untuk kode kritis. Kode yang dibutuhkan saat halaman pertama tampil harus static import, kalau tidak halaman justru telat render.
- Selalu tangani kegagalan. Dynamic import bisa gagal karena jaringan. Tanpa try/catch, promise rejection yang tidak ditangani akan muncul sebagai error misterius.
- Over-splitting. Memecah terlalu agresif (misalnya per fungsi kecil) justru menambah banyak request HTTP kecil yang lambat. Split per fitur atau per route, bukan per fungsi.
export default function App(): JSX.Element { return <h1>Hello world</h1> }
Tantangan
Fitur Peta On-Demand
Di dunia nyata, memuat library peta (ratusan KB) di halaman utama membuat semua pengunjung menunggu padahal hanya 5 persen yang membuka halaman lokasi. Tantangan ini melatihmu memangkas waktu muat awal dengan code splitting. Buat fungsi async bukaPeta() yang melakukan dynamic import("./peta.js"), memanggil tampilkanPeta("Jakarta") dari modul tersebut, menampilkan pesan loading selama modul diunduh, dan menangani kegagalan dengan pesan yang ramah.
<!doctype html> <html> <head> <meta charset="utf-8" /> </head> <body> <h1>Halo JS</h1> <p>Buka console preview untuk melihat output.</p> <script src="index.js"></script> </body> </html>