Skip to content

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

Vue 3 Teleport 傳送門

在開發複雜的前端介面時,我們經常會遇到 邏輯在內層,樣式在外層 的矛盾。最典型的範例就是 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 位置變了,但內部的 ref()reactive() 以及從父層接收的 props 依然維持響應式連動。
  • 多個傳送門:多個 <teleport> 可以同時指向同一個目標元素,如同一個 to="body",Vue 會按照順序將它們依序掛載。
  • Disabled 模式:您可以透過 :disabled="true" 屬性暫時停用傳送功能,讓內容回到原本的位置。
html
<!-- 桌機把彈窗送到 body,手機版留在原地當內嵌區塊 -->
<teleport to="body" :disabled="isMobile">
  <div class="modal">...</div>
</teleport>

目標必須先存在

to 指定的元素必須在 Teleport 掛載時就已經存在於頁面上,否則會找不到目標而發出警告。若目標是由 Vue 自己渲染的,記得確認它比 Teleport 更早掛載。

總結

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

常見問題

Teleport 是用來解決什麼問題的?

解決彈窗、提示框這類元件被祖先元素的 overflow: hiddenz-index 堆疊環境限制的問題。邏輯仍寫在內層元件,但實際 DOM 渲染到 <body> 或指定容器。

被傳送出去的內容還吃得到 scoped 樣式嗎?

可以。scoped 樣式是編譯期在元素上加屬性標記,與 DOM 最終位置無關,因此傳送後樣式依然有效。

傳送後元件的狀態會重置嗎?

不會。Teleport 只移動 DOM 節點的位置,元件實體本身沒有被銷毀,內部的 refreactiveprops 都維持原本的響應式連動。

to 屬性可以指定哪些目標?

任何 CSS 選擇器字串或實際的 DOM 元素,例如:to="body"to="#modals"。目標必須在 Teleport 掛載時就已存在於頁面上。

什麼時候會用到 disabled?

需要依情境切換渲染位置時,例如:桌機版把彈窗送到 <body>,手機版則留在原地當作內嵌區塊,只要動態綁定 disabled 即可。

多個 Teleport 指向同一個目標會怎麼排列?

會依照掛載的先後順序依序附加在目標容器內,後掛載的排在後面,不會互相覆蓋。

延伸閱讀