<Teleport> dan <KeepAlive> adalah dua komponen bawaan Vue. Teleport merender sebagian template di tempat lain di DOM, misalnya modal "Pesanan berhasil!" langsung di <body>. KeepAlive menyimpan komponen yang sedang gak tampil, jadi isinya gak hilang saat tab berganti. Bayangin kasir Warung Bu Sri. Pengumuman "pesanan siap" ditulis di meja kasir, tapi ditempel di papan depan warung supaya semua orang melihatnya; isinya tetap urusan kasir. Itu Teleport. Lalu saat kasir pindah melayani pembeli lain, catatan pesanan yang setengah ditulis dimasukkan ke laci, bukan dibuang. Itu KeepAlive.
🧪 Tebakan cepat: Tab Makanan punya kolom catatan. Pembeli mengetik "tanpa sayur", pindah ke tab Minuman, lalu kembali ke tab Makanan. Tanpa KeepAlive, apa isi kolom catatannya? Tebak dulu, jawabannya ada di bagian "KeepAlive: tab yang gak lupa".
Teleport: modal pindah ke body
Modal "Pesanan berhasil!" ditulis di dalam kartu pesanan, karena kartu itulah yang tahu kapan pesanan berhasil. Masalahnya, kartu itu punya overflow: hidden dan transform. Modal position: fixed di dalamnya ikut terkurung: ukurannya mengikuti kartu dan sisanya terpotong. Ini saudara masalah stacking context dari pelajaran z-index. Teleport memindahkan DOM modalnya ke <body>, sedangkan kodenya tetap di kartu.
<script setup>
import { ref } from "vue"
const tampil = ref(false)
</script>
<template>
<div class="kartu-pesanan">
<button @click="tampil = true">Pesan</button>
<Teleport to="body">
<div v-if="tampil" class="modal">
<p>Pesanan berhasil!</p>
<button @click="tampil = false">Tutup</button>
</div>
</Teleport>
</div>
</template>
Sesudah Pesan diklik, DOM-nya begini:
<body>
<div id="app">
<div class="kartu-pesanan">
<button>Pesan</button>
</div>
</div>
<div class="modal">
<p>Pesanan berhasil!</p>
<button>Tutup</button>
</div>
</body>
todiisi selector CSS, seperti"body"atau"#modal-tujuan".- Yang pindah cuma DOM-nya. Logikanya tetap milik komponen:
tampil,@click, props, dan provide/inject jalan seperti biasa. - Target harus sudah ada di halaman saat komponennya dipasang. Dengan
to="#tidak-ada", build development menulis[Vue warn]: Failed to locate Teleport target with selector "#tidak-ada"., dan membuka modalnya melempar error. :disabled="true"membuat Teleport merender isinya di tempat aslinya, misalnya modal yang di layar kecil sengaja tampil di dalam kartu.- Latihan pertama: tombol
#opendan<div id="modal">berisi "Ini modal!" plus tombol Tutup, dibungkus<Teleport to="body">. Pemeriksanya membuka modalnya dan mencaribody > #modal, jadi modal yang tetap di dalam komponennya gak lulus.
KeepAlive: tab yang gak lupa
<!-- App.vue -->
<script setup>
import { ref } from "vue"
import TabMakanan from "./TabMakanan.vue"
import TabMinuman from "./TabMinuman.vue"
const tab = ref("makanan")
const tabs = { makanan: TabMakanan, minuman: TabMinuman }
</script>
<template>
<button @click="tab = 'makanan'">Makanan</button>
<button @click="tab = 'minuman'">Minuman</button>
<KeepAlive>
<component :is="tabs[tab]" />
</KeepAlive>
</template>
<!-- TabMakanan.vue -->
<script setup>
import { ref, onMounted, onUnmounted, onActivated, onDeactivated } from "vue"
const catatan = ref("")
onMounted(() => console.log("mounted"))
onUnmounted(() => console.log("unmounted"))
onActivated(() => console.log("activated"))
onDeactivated(() => console.log("deactivated"))
</script>
<template>
<p>Nasi Goreng Rp15000, Mie Ayam Rp12000</p>
<input v-model="catatan" placeholder="Catatan" />
</template>
| Langkah | Tanpa KeepAlive | Dengan KeepAlive |
|---|---|---|
| Tab Makanan tampil | mounted |
mounted, activated |
| Ketik "tanpa sayur", pindah ke Minuman | unmounted |
deactivated |
| Kembali ke Makanan | mounted |
activated |
| Isi kolom catatan | kosong | tanpa sayur |
Tanpa KeepAlive, kolom catatannya kosong. Itu jawaban tebakannya. TabMakanan di-unmount saat ditinggalkan, persis seperti v-if di pelajaran lifecycle, dan yang tampil saat kembali adalah TabMakanan baru dengan catatan baru.
<component :is="tabs[tab]">merender komponen yang dipilih data. Gantitab, komponennya berganti.- Di dalam KeepAlive, komponen yang ditinggalkan disimpan, bukan dihancurkan.
onDeactivatedjalan saat ditinggalkan,onActivatedsaat kembali (dan sekali sesudahonMountedyang pertama).onMountedgak jalan lagi. v-if/v-elseantar dua komponen juga boleh di dalam KeepAlive, asal yang tampil cuma satu komponen.- Latihan kedua adalah contoh di atas dalam bentuk kecil:
TabMakananberisi<input id="catatan">,TabMinumanberisi#minuman, dan<KeepAlive>membungkus<component :is="activeTab === 1 ? TabMakanan : TabMinuman" />. Pemeriksanya mengetiktanpa sayur, pindah ke Minuman, kembali, lalu membaca catatannya.
include dan max
<KeepAlive include="TabMakanan">cuma menyimpan komponen bernama TabMakanan; yang lain tetap di-unmount. Namanya dari opsiname, atau dari nama file untuk<script setup>(TabMakanan.vue jadi TabMakanan). Boleh array::include="['TabMakanan', 'TabMinuman']".excludekebalikannya.:max="2"menyimpan paling banyak 2 komponen, termasuk yang sedang tampil. Kalau tab ketiga dibuka, yang paling lama gak dibuka di-unmount.
⚠️ Jebakan yang sering ketemu
- Target Teleport belum ada. Selector-nya harus menunjuk elemen yang sudah ada sebelum komponennya dipasang, idealnya di luar aplikasi Vue, seperti
body. - Mengira Teleport mengubah logika. Ia cuma memindahkan DOM. Props, event, dan data tetap milik komponen yang menulisnya.
- Menunggu
onMountedsaat kembali ke tab. Komponen yang disimpan KeepAlive gak di-mount ulang. PakaionActivated. includeyang namanya gak cocok. Nama harus sama persis dengan nama komponennya, termasuk huruf besarnya.- Menyimpan semuanya. Tiap komponen yang disimpan tetap memakai memori. Batasi dengan
max.
TL;DR: <Teleport to="body"> merender isinya di tempat lain di DOM, biasanya <body>, supaya modal gak terkurung overflow atau transform parent-nya; logikanya tetap milik komponen, targetnya harus sudah ada, dan disabled mengembalikannya ke tempat asal. <KeepAlive> menyimpan komponen yang ditinggalkan, jadi state seperti isi kolom tetap ada saat kembali; tanpanya komponen di-unmount dan state-nya hilang. Komponen yang disimpan memakai onActivated dan onDeactivated, bukan onMounted lagi. include memilih komponen mana yang disimpan, max membatasi jumlahnya.