Skip to content

Vue 3 實戰:掌握 Teleport 傳送門,完美解決 Modal 彈窗與 CSS 層級限制

在開發複雜的前端介面時,我們經常會遇到 邏輯在內層,樣式在外層 的矛盾。最典型的範例就是 Modal(彈窗) 或 Tooltip(提示框)。

Vue 3 提供的 Teleport 元件,能讓您將元件的 HTML 結構 傳送 到 DOM 階層以外的指定位置(例如 <body> 標籤直屬下方),同時還能完整保留該元件內部的資料狀態與邏輯。

使用 Teleport 的理由

如果在一個深度嵌套的元件裡寫了一個全螢幕彈窗(Modal),會遇到以下麻煩:

  • CSS 限制:如果外層元件設定了 overflow: hiddenz-index,彈窗可能會被遮擋或切掉。
  • DOM 層級混亂:為了讓彈窗蓋住全螢幕,可能被迫把彈窗元件寫在 App.vue 最外層,這會導致邏輯分散,按鈕在內層元件,彈窗卻在外層元件。

核心價值

Teleport 讓您實現了 邏輯封裝與視圖分離:您可以在內層元件編寫彈窗程式碼(方便管理邏輯),但最終渲染出來的 DOM 卻出現在 <body> 或您指定的任何地方。

基本用法:指定您的傳送目標

<teleport> 接收一個 to 屬性,用來指定目標容器。這個值可以是任何 CSS 選取器,如 to="body"to="#app-modals" 或是實際的 DOM 元素

實戰範例:封裝一個不受限的 Modal

父元件呼叫方式

vue
<!-- /views/Slot.vue -->
<script setup>
import Modal from "@/components/Modal.vue";
</script>

<template>
  <Modal />
</template>
vue
<!-- /components/Modal.vue -->
<script setup>
import { ref } from "vue";

const open = ref(false);
</script>

<template>
  <button @click="open = true">Open Modal</button>

  <teleport to="body">
    <div v-if="open" class="modal">
      <p>Hello from the modal!</p>
      <button @click="open = false">Close</button>
    </div>
  </teleport>
</template>

<style scoped>
.modal {
  position: fixed;
  z-index: 999;
  top: 20%;
  left: 50%;
  width: 300px;
  margin-left: -150px;
}
</style>

Teleport 的關鍵特性

  • 保留狀態:雖然 DOM 位置變了,但內部的 refreactive 以及從父層接收的 props 依然維持響應式連動。
  • 多個傳送門:多個 <teleport> 可以同時指向同一個目標元素,如同一個 to="body",Vue 會按照順序將它們依序掛載。
  • Disabled 模式:您可以透過 :disabled="true" 屬性暫時停用傳送功能,讓內容回到原本的位置。

總結

Teleport 是 Vue 3 中處理彈窗、通知與下拉選單的標準解答。它完美解決了 CSS 層級競爭問題,同時確保了元件開發的封裝性,讓您的 UI 架構更加穩健且易於調試。