Teleport & KeepAlive — Vue

Teleport Render konten di tempat lain di DOM tree: ---template--- Buka Modal Ini modal! KeepAlive Menyimpan state komponen yang di-toggle — tidak di-destroy…

<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>

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.

include dan max

⚠️ Jebakan yang sering ketemu

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.

Yang akan kamu pelajari