Vue 3 Teleport:把 DOM 送到元件樹之外
<Teleport>把一段範本渲染到目前元件 DOM 樹之外的指定位置,同時保留 Vue 元件樹中的狀態與邏輯階層。
<Teleport> 是 Vue 3 的內建元件(Built-in Component)。邏輯上它仍屬於某個子元件,視覺上卻可以把 DOM 掛到 <body> 或其他容器,避免被父層 CSS 裁切或壓在錯誤的層級。
一、解決的核心痛點
有些 UI 在邏輯上屬於深層子元件,在視覺與 DOM 結構上卻比較適合放在最外層(例如 <body> 底下):
- 模態框(Modal / Dialog)
- 全域通知(Toast / Notification)
- 浮動選單(Tooltip / Dropdown)
若直接嵌在深層父元件的 DOM 內,容易被父層 CSS 干擾:
overflow: hidden:彈窗內容被父容器裁切。z-index層疊上下文(Stacking Context):父層有transform、filter或較低的z-index時,彈窗蓋不過頁面上其他元素。
<Teleport> 把實際 DOM 搬到目標節點,上述限制就不再綁在原本的父容器上。
二、基本語法與範例
to 指定目標 DOM 節點,可為 CSS 選擇器(如 body、#modals)或實際的 DOM 物件。
<script setup>
import { ref } from 'vue'
const open = ref(false)
</script>
<template>
<button @click="open = true">開啟彈跳視窗</button>
<!-- 將內部內容傳送到 <body> 標籤的最下方 -->
<Teleport to="body">
<div v-if="open" class="modal-overlay">
<div class="modal-content">
<p>這是一個不受父層 CSS 限制的 Modal!</p>
<button @click="open = false">關閉</button>
</div>
</div>
</Teleport>
</template>
<style scoped>
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
}
.modal-content {
background: white;
padding: 20px;
border-radius: 8px;
}
</style>
v-if 仍寫在 Teleport 內部即可:關閉時不會在 body 留下空節點。
三、重要特性
1. 邏輯與狀態依舊在原地
DOM 被搬到 <body>,Vue 元件樹的層級不變。內容仍可存取父元件的 props、ref,也能 $emit 事件給父元件。
2. 動態禁用(disabled)
:disabled="isMobile" 可動態決定是否傳送。為 true 時內容留在原本位置渲染,常用於響應式(例如手機版內嵌、桌面版彈窗)。
3. 多個 Teleport 聚集到同一目標
多個元件都傳送到同一個目標(如 to="#modals")時,內容會依掛載順序依序附加在該目標內。
四、總結
| 面向 | 行為 |
|---|---|
| DOM | 渲染到 to 指定的節點 |
| 元件樹 | 邏輯、狀態、事件仍屬於原本父元件 |
| 典型用途 | Modal、Toast、Tooltip,避開 overflow 與 stacking context |
官方文件:Teleport | Vue.js