<Teleport> 是一(yī)個內(nèi)置組件,它可(kě)以将一(yī)個組件內(nèi)部的一(yī)部分模闆“傳送”到該組件的 DOM 結構外(wài)層的位置去。
基本用法
有時(shí)我們可(kě)能(néng)會遇到這(zhè)樣的場景:一(yī)個組件模闆的一(yī)部分在邏輯上(shàng)從(cóng)屬于該組件,但(dàn)從(cóng)整個應用視(shì)圖的角度來看(kàn),它在 DOM 中應該被渲染在整個 Vue 應用外(wài)部的其他(tā)地(dì)方。
這(zhè)類場景最常見(jiàn)的例子就是全屏的模态框。理想情況下(xià),我們希望觸發模态框的按鈕和(hé)模态框本身是在同一(yī)個組件中,因為(wèi)它們都(dōu)與組件的開(kāi)關狀态有關。但(dàn)這(zhè)意味着該模态框将與按鈕一(yī)起渲染在應用 DOM 結構裏很(hěn)深的地(dì)方。這(zhè)會導緻該模态框的 CSS 布局代碼很(hěn)難寫。
試想下(xià)面這(zhè)樣的 HTML 結構:
template
<div class="outer">
<h3>Tooltips with Vue 3 Teleport</h3>
<div>
<MyModal />
</div>
</div>
接下(xià)來我們來看(kàn)看(kàn) <MyModal> 的實現:
vue
<script>
export default {
data() {
return {
open: 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>
這(zhè)個組件中有一(yī)個 <button> 按鈕來觸發打開(kāi)模态框,和(hé)一(yī)個 class 名為(wèi) .modal 的 <div>,它包含了(le)模态框的內(nèi)容和(hé)一(yī)個用來關閉的按鈕。
當在初始 HTML 結構中使用這(zhè)個組件時(shí),會有一(yī)些潛在的問(wèn)題:
position: fixed 能(néng)夠相對于浏覽器窗口放置有一(yī)個條件,那(nà)就是不能(néng)有任何祖先元素設置了(le) transform、perspective 或者 filter 樣式屬性。也就是說如(rú)果我們想要(yào)用 CSS transform 為(wèi)祖先節點 <div class="outer"> 設置動畫(huà),就會不小心破壞模态框的布局!
這(zhè)個模态框的 z-index 受限于它的容器元素。如(rú)果有其他(tā)元素與 <div class="outer"> 重疊并有更高的 z-index,則它會覆蓋住我們的模态框。
<Teleport> 提供了(le)一(yī)個更簡單的方式來解決此類問(wèn)題,讓我們不需要(yào)再顧慮 DOM 結構的問(wèn)題。讓我們用 <Teleport> 改寫一(yī)下(xià) <MyModal>:
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> 接收一(yī)個 to prop 來指定傳送的目标。to 的值可(kě)以是一(yī)個 CSS 選擇器字符串,也可(kě)以是一(yī)個 DOM 元素對象。這(zhè)段代碼的作(zuò)用就是告訴 Vue“把以下(xià)模闆片段傳送到 body 标簽下(xià)”。
你可(kě)以點擊下(xià)面這(zhè)個按鈕,然後通(tōng)過浏覽器的開(kāi)發者工(gōng)具,在 <body> 标簽下(xià)找到模态框元素:
Open Modal
我們也可(kě)以将 <Teleport> 和(hé) <Transition> 結合使用來創建一(yī)個帶動畫(huà)的模态框。你可(kě)以看(kàn)看(kàn)這(zhè)個示例。
TIP
<Teleport> 挂載時(shí),傳送的 to 目标必須已經存在于 DOM 中。理想情況下(xià),這(zhè)應該是整個 Vue 應用 DOM 樹(shù)外(wài)部的一(yī)個元素。如(rú)果目标元素也是由 Vue 渲染的,你需要(yào)确保在挂載 <Teleport> 之前先挂載該元素。
搭配組件使用
<Teleport> 隻改變了(le)渲染的 DOM 結構,它不會影響組件間(jiān)的邏輯關系。也就是說,如(rú)果 <Teleport> 包含了(le)一(yī)個組件,那(nà)麽該組件始終和(hé)這(zhè)個使用了(le) <teleport> 的組件保持邏輯上(shàng)的父子關系。傳入的 props 和(hé)觸發的事件也會照常工(gōng)作(zuò)。
這(zhè)也意味着來自(zì)父組件的注入也會按預期工(gōng)作(zuò),子組件将在 Vue Devtools 中嵌套在父級組件下(xià)面,而不是放在實際內(nèi)容移動到的地(dì)方。
禁用 Teleport
在某些場景下(xià)可(kě)能(néng)需要(yào)視(shì)情況禁用 <Teleport>。舉例來說,我們想要(yào)在桌面端将一(yī)個組件當做(zuò)浮層來渲染,但(dàn)在移動端則當作(zuò)行(xíng)內(nèi)組件。我們可(kě)以通(tōng)過對 <Teleport> 動态地(dì)傳入一(yī)個 disabled prop 來處理這(zhè)兩種不同情況。
template
<Teleport :disabled="isMobile">
...
</Teleport>
這(zhè)裏的 isMobile 狀态可(kě)以根據 CSS media query 的不同結果動态地(dì)更新。
多個 Teleport 共享目标
一(yī)個可(kě)重用的模态框組件可(kě)能(néng)同時(shí)存在多個實例。對于此類場景,多個 <Teleport> 組件可(kě)以将其內(nèi)容挂載在同一(yī)個目标元素上(shàng),而順序就是簡單的順次追加,後挂載的将排在目标元素下(xià)更後面的位置上(shàng)。
比如(rú)下(xià)面這(zhè)樣的用例:
template
<Teleport to="#modals">
<div>A</div>
</Teleport>
<Teleport to="#modals">
<div>B</div>
</Teleport>
渲染的結果為(wèi):
html
<div id="modals">
<div>A</div>
<div>B</div>
</div>
網站建設開(kāi)發|APP設計開(kāi)發|小程序建設開(kāi)發