Async component adalah komponen yang kodenya baru diunduh saat pertama kali dibutuhkan, bukan ikut di unduhan awal halaman. Vue membuatnya dengan defineAsyncComponent(). Bayangin buku riwayat pesanan Warung Bu Sri: tebal dan jarang dibuka. Bu Sri gak membawanya ke meja kasir tiap pagi. Buku itu disimpan di gudang dan baru diambil saat ada pembeli yang menanyakan pesanan lamanya. Pertama kali butuh waktu sebentar untuk mengambilnya; sesudah itu bukunya sudah di meja.
🧪 Tebakan cepat:
RiwayatPesanandibuat dengandefineAsyncComponentdan diberiloadingComponentbertuliskan "Memuat riwayat...", tanpa opsidelay. Unduhannya selesai dalam 150 ms. Apakah "Memuat riwayat..." sempat tampil? Tebak dulu, jawabannya ada di bagian "Loading, error, delay, timeout".
defineAsyncComponent: diunduh saat pertama dirender
<script setup>
import { ref, defineAsyncComponent } from "vue"
const RiwayatPesanan = defineAsyncComponent(() => import("./RiwayatPesanan.vue"))
const buka = ref(false)
</script>
<template>
<button @click="buka = !buka">Riwayat pesanan</button>
<RiwayatPesanan v-if="buka" />
</template>
| Kejadian | Yang diunduh | Yang tampil |
|---|---|---|
| Halaman dibuka | bundle utama saja | tombol Riwayat pesanan |
| Klik pertama | file terpisah, misalnya RiwayatPesanan-a1b2c3.js |
belum ada apa-apa sampai unduhannya selesai |
| Unduhan selesai | gak ada | daftar riwayat |
| Tutup, lalu buka lagi | gak ada, sudah tersimpan | daftar riwayat, langsung |
import("./RiwayatPesanan.vue")dengan tanda kurung adalah dynamic import: ia mengembalikan Promise. Bundler seperti Vite memisahkan komponen itu ke file sendiri. Ini disebut code splitting: bundle awal lebih kecil, jadi halaman pertama lebih cepat tampil.() =>di depannya penting. Fungsi itu disebut loader, dan Vue baru memanggilnya saatRiwayatPesananpertama kali dirender, di sini saatbukajaditrue.- Loader dipanggil sekali, lalu hasilnya disimpan. Kalau unduhannya gagal, loader dicoba lagi saat komponennya dirender berikutnya.
- Di editor latihan dan Playground gak ada file
.vueuntuk di-import. Loader cukup fungsi yang mengembalikan Promise berisi komponen, dandefineAsyncComponentdipanggil lewatVue:
const buka = ref(false)
const RiwayatPesanan = Vue.defineAsyncComponent(() =>
new Promise((resolve) => {
setTimeout(() => resolve({ template: "<ul><li>Nasi Goreng Rp15000</li></ul>" }), 1000)
})
)
---template---
<button @click="buka = !buka">Riwayat pesanan</button>
<component v-if="buka" :is="RiwayatPesanan" />
- Latihan kedua memakai bentuk di atas tanpa jeda:
const Lazy = Vue.defineAsyncComponent(() => Promise.resolve({ template: ... })), lalu<component v-if="loaded" :is="Lazy" />yang baru tampil sesudah tombol#loadmenyalakanloaded. Pemeriksanya juga mencaridefineAsyncComponent(di kodemu.
Loading, error, delay, timeout
Selama mengunduh, async component gak menampilkan apa-apa. Untuk memberi tahu pembeli, pakai bentuk objek:
const RiwayatPesanan = defineAsyncComponent({
loader: () => import("./RiwayatPesanan.vue"),
loadingComponent: MemuatRiwayat, // tampil selama mengunduh
delay: 200, // bawaan: tunggu 200 ms dulu sebelum loadingComponent tampil
errorComponent: GagalMemuat, // tampil kalau unduhannya gagal
timeout: 3000, // dianggap gagal kalau lewat 3 detik
})
| Unduhan | Yang tampil |
|---|---|
| selesai dalam 150 ms | kosong sebentar, lalu daftar riwayat. "Memuat riwayat..." gak sempat tampil |
| selesai dalam 1,5 detik | kosong 200 ms, lalu "Memuat riwayat...", lalu daftar riwayat |
| gagal | "Riwayat gagal dimuat" dari errorComponent; tanpa errorComponent, tempatnya tetap kosong, atau "Memuat riwayat..." gak pernah hilang. Error-nya selalu ditulis di console |
1,5 detik dengan timeout: 1000 |
errorComponent di detik pertama, lalu daftar riwayat saat unduhannya akhirnya selesai |
Jawaban tebakannya: gak sempat. Itu jawaban tebakannya. delay bawaannya 200 ms, dan unduhan 150 ms sudah selesai sebelum itu. Delay ini sengaja: di koneksi cepat, tulisan loading yang muncul sekejap lalu hilang malah terasa seperti kedipan. Tulis delay: 0 kalau loading harus langsung tampil.
Suspense: satu fallback untuk yang async
Komponen yang setup-nya menunggu sesuatu, misalnya <script setup> dengan await di baris paling atas, disebut komponen dengan async setup. Ia harus dibungkus <Suspense>:
<!-- RiwayatPesanan.vue -->
<script setup>
const res = await fetch("/api/riwayat")
const daftar = await res.json()
</script>
<!-- App.vue -->
<template>
<Suspense>
<RiwayatPesanan />
<template #fallback>
<p>Memuat riwayat...</p>
</template>
</Suspense>
</template>
<Suspense>menampilkan slot#fallbacksampai semua yang async di dalamnya selesai, lalu menukarnya dengan isi aslinya.- Tanpa
<Suspense>, komponen dengan async setup gak pernah tampil. Build development menulis[Vue warn]: Component <Anonymous>: setup function returned a promise, but no <Suspense> boundary was found in the parent component tree. - Async component di dalam
<Suspense>ikut diatur Suspense: fallback-nya yang tampil, danloadingComponentsertadelaymiliknya diabaikan. <Suspense>masih ditandai eksperimental. Build development menulis<Suspense> is an experimental feature and its API will likely change.- Latihan pertama memakai Suspense sungguhan. Starter-nya punya
Riwayat, objek denganasync setup()yang menunggu 0,1 detik. Kamu membungkus<component :is="Riwayat" />dengan<Suspense>dan menulis<template #fallback><div id="loading">Memuat...</div></template>. Tanpa<Suspense>,Riwayatgak pernah tampil.
⚠️ Jebakan yang sering ketemu
- Semuanya dibuat async. Komponen kecil yang tampil di setiap halaman cuma menambah unduhan terpisah. Pilih yang besar dan jarang dibuka: riwayat pesanan, grafik, halaman admin.
- Lupa
() =>.defineAsyncComponent(import("./RiwayatPesanan.vue"))menjalankanimportsaat file dibaca, jadi gak ada yang ditunda, dan Vue berhenti denganloader is not a function. - Tanpa
errorComponent. Unduhan yang gagal meninggalkan tempat kosong, atau tulisan loading yang gak pernah hilang. - Async setup tanpa
<Suspense>. Komponennya gak pernah tampil. - Mengira loading langsung tampil.
delaybawaannya 200 ms.
TL;DR: defineAsyncComponent(() => import("./RiwayatPesanan.vue")) membuat komponen yang kodenya diunduh terpisah saat pertama dirender, lalu disimpan; itu code splitting, dan bundle awal jadi lebih kecil. Bentuk objeknya menambah loadingComponent (tampil sesudah delay, bawaan 200 ms), errorComponent, dan timeout. <Suspense> dengan slot #fallback menunggu komponen dengan async setup dan async component di dalamnya; tanpa Suspense, async setup gak pernah tampil. Pakai async component untuk bagian yang besar dan jarang dipakai.