Teleport
<Teleport>
是一個內建元件,允許我們將元件模板的一部分 "傳送" 到一個位於該元件DOM層次結構之外的DOM節點。
基本用法
有時我們可能會遇到以下場景:元件模板的一部分在邏輯上屬於它,但從視覺角度來看,它應該顯示在DOM中的其他位置,在Vue應用程式之外。
這種情況最常見的例子是在構建全屏模態時。理想情況下,我們希望模態的按鈕和模態本身位於同一個元件中,因為它們都與模態的開啟/關閉狀態相關。但這意味著模態將與按鈕一起渲染,深度巢狀在應用程式的DOM層次結構中。這可能會在透過CSS定位模態時產生一些棘手的問題。
考慮以下HTML結構。
template
<div class="outer">
<h3>Vue Teleport Example</h3>
<div>
<MyModal />
</div>
</div>
以下是 <MyModal>
的實現
vue
<script setup>
import { ref } from 'vue'
const open = ref(false)
</script>
<template>
<button @click="open = true">Open Modal</button>
<div v-if="open" class="modal">
<p>Hello from the modal!</p>
<button @click="open = false">Close</button>
</div>
</template>
<style scoped>
.modal {
position: fixed;
z-index: 999;
top: 20%;
left: 50%;
width: 300px;
margin-left: -150px;
}
</style>
該元件包含一個 <button>
用於觸發模態的開啟,一個帶有 .modal
類的 <div>
,其中將包含模態的內容和一個按鈕來自動關閉。
當在初始HTML結構中使用此元件時,存在一些潛在問題
position: fixed
僅在沒有任何祖先元素設定transform
、perspective
或filter
屬性時將元素定位到視口相對位置。例如,如果我們打算使用CSS轉換來動畫化祖先<div class="outer">
,這將破壞模態佈局!模態的
z-index
受其容器元素的約束。如果有其他元素與<div class="outer">
相重疊並具有更高的z-index
,它將覆蓋我們的模態。
<Teleport>
提供了一種簡潔的方法來解決這個問題,透過允許我們跳出巢狀的DOM結構。讓我們修改 <MyModal>
以使用 <Teleport>
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>
<Teleport>
的 to
目標期望一個CSS選擇器字串或一個實際的DOM節點。在這裡,我們實際上是告訴Vue "傳送 這個模板片段到 body
標籤"。
您可以透過點選下面的按鈕並使用瀏覽器開發工具檢查 <body>
標籤
您可以將 <Teleport>
與 <Transition>
結合使用來建立動畫模態框 - 請參閱 此處示例。
技巧
teleport 的 to
目標必須在 <Teleport>
元件掛載時已經在 DOM 中。理想情況下,這應該是一個整個 Vue 應用程式之外的外部元素。如果您要針對由 Vue 渲染的另一個元素,請確保在該元素掛載之前掛載 <Teleport>
。
與元件一起使用
<Teleport>
只會更改渲染的 DOM 結構 - 它不會影響元件的邏輯層次結構。也就是說,如果 <Teleport>
包含一個元件,那麼該元件將仍然是包含 <Teleport>
的父元件的邏輯子元件。屬性傳遞和事件發射將繼續以相同的方式工作。
這也意味著父元件的注入將按預期工作,並且子元件將在 Vue Devtools 中位於父元件下方,而不是實際內容移動到的位置。
停用 Teleport
在某些情況下,我們可能希望有條件地停用 <Teleport>
。例如,我們可能希望將元件作為桌面上的覆蓋層渲染,但在移動端上內聯渲染。 <Teleport>
支援 disabled
屬性,可以動態切換
template
<Teleport :disabled="isMobile">
...
</Teleport>
其中 isMobile
狀態可以透過檢測媒體查詢更改來動態更新。
同一目標上的多個 Teleport
一個常見的用例是可重用的 <Modal>
元件,可能存在多個例項同時活躍。在這種情況下,多個 <Teleport>
元件可以將它們的內容掛載到同一目標元素上。順序將是簡單的追加 -較晚掛載的將位於目標元素中較早掛載的後面。
給定以下用法
template
<Teleport to="#modals">
<div>A</div>
</Teleport>
<Teleport to="#modals">
<div>B</div>
</Teleport>
渲染結果將是
html
<div id="modals">
<div>A</div>
<div>B</div>
</div>
延遲 Teleport
在 Vue 3.5 及以上版本中,我們可以使用 defer
屬性將 Teleport 的目標解析延遲到應用程式的其他部分已掛載。這允許 Teleport 針對一個由 Vue 渲染但位於元件樹較晚部分的容器元素。
template
<Teleport defer to="#late-div">...</Teleport>
<!-- somewhere later in the template -->
<div id="late-div"></div>
請注意,目標元素必須在與 Teleport 同一掛載/更新週期中渲染 - 即如果 <div>
只在兩秒後掛載,Teleport 仍然會報告錯誤。defer 與 mounted
生命週期鉤子的工作方式相似。
相關