做自(zì)由與創造的先行者

Teleport

Vue.js中文(wén)手冊

<Teleport> 是一個内置組件,它可以将一個組件内部的一部分模闆“傳送”到(dào)該組件的 DOM 結構外(wài)層的位置去。

基本用(yòng)法 ​

有時(shí)我們可能(néng)會(huì)遇到(dào)這(zhè)樣的場景:一個組件模闆的一部分在邏輯上(shàng)從(cóng)屬于該組件,但(dàn)從(cóng)整個應用(yòng)視(shì)圖的角度來(lái)看(kàn),它在 DOM 中應該被渲染在整個 Vue 應用(yòng)外(wài)部的其他(tā)地方。

這(zhè)類場景最常見的例子就是全屏的模态框。理(lǐ)想情況下(xià),我們希望觸發模态框的按鈕和(hé)模态框本身是在同一個組件中,因爲它們都與組件的開(kāi)關狀态有關。但(dàn)這(zhè)意味着該模态框将與按鈕一起渲染在應用(yòng) DOM 結構裏很(hěn)深的地方。這(zhè)會(huì)導緻該模态框的 CSS 布局代碼很(hěn)難寫。

試想下(xià)面這(zhè)樣的 HTML 結構:

template

<div class="outer">

<h3>Tooltips with Vue 3 Teleport</h3>

<div>

<MyModal />

</div>

</div>

接下(xià)來(lái)我們來(lái)看(kàn)看(kàn) <MyModal> 的實現(xiàn):

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è)個組件中有一個 <button> 按鈕來(lái)觸發打開(kāi)模态框,和(hé)一個 class 名爲 .modal 的 <div>,它包含了(le)模态框的内容和(hé)一個用(yòng)來(lái)關閉的按鈕。

當在初始 HTML 結構中使用(yòng)這(zhè)個組件時(shí),會(huì)有一些(xiē)潛在的問題:

position: fixed 能(néng)夠相對(duì)于浏覽器窗口放(fàng)置有一個條件,那就是不能(néng)有任何祖先元素設置了(le) transform、perspective 或者 filter 樣式屬性。也(yě)就是說如果我們想要用(yòng) CSS transform 爲祖先節點 <div class="outer"> 設置動畫(huà),就會(huì)不小(xiǎo)心破壞模态框的布局!

這(zhè)個模态框的 z-index 受限于它的容器元素。如果有其他(tā)元素與 <div class="outer"> 重疊并有更高(gāo)的 z-index,則它會(huì)覆蓋住我們的模态框。

<Teleport> 提供了(le)一個更簡單的方式來(lái)解決此類問題,讓我們不需要再顧慮 DOM 結構的問題。讓我們用(yòng) <Teleport> 改寫一下(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> 接收一個 to prop 來(lái)指定傳送的目标。to 的值可以是一個 CSS 選擇器字符串,也(yě)可以是一個 DOM 元素對(duì)象。這(zhè)段代碼的作(zuò)用(yòng)就是告訴 Vue“把以下(xià)模闆片段傳送到(dào) body 标簽下(xià)”。

你(nǐ)可以點擊下(xià)面這(zhè)個按鈕,然後通過浏覽器的開(kāi)發者工(gōng)具,在 <body> 标簽下(xià)找到(dào)模态框元素:

Open Modal

我們也(yě)可以将 <Teleport> 和(hé) <Transition> 結合使用(yòng)來(lái)創建一個帶動畫(huà)的模态框。你(nǐ)可以看(kàn)看(kàn)這(zhè)個示例。

TIP

<Teleport> 挂載時(shí),傳送的 to 目标必須已經存在于 DOM 中。理(lǐ)想情況下(xià),這(zhè)應該是整個 Vue 應用(yòng) DOM 樹外(wài)部的一個元素。如果目标元素也(yě)是由 Vue 渲染的,你(nǐ)需要确保在挂載 <Teleport> 之前先挂載該元素。

搭配組件使用(yòng) ​

<Teleport> 隻改變了(le)渲染的 DOM 結構,它不會(huì)影響組件間的邏輯關系。也(yě)就是說,如果 <Teleport> 包含了(le)一個組件,那麽該組件始終和(hé)這(zhè)個使用(yòng)了(le) <teleport> 的組件保持邏輯上(shàng)的父子關系。傳入的 props 和(hé)觸發的事(shì)件也(yě)會(huì)照常工(gōng)作(zuò)。

這(zhè)也(yě)意味着來(lái)自(zì)父組件的注入也(yě)會(huì)按預期工(gōng)作(zuò),子組件将在 Vue Devtools 中嵌套在父級組件下(xià)面,而不是放(fàng)在實際内容移動到(dào)的地方。

禁用(yòng) Teleport ​

在某些(xiē)場景下(xià)可能(néng)需要視(shì)情況禁用(yòng) <Teleport>。舉例來(lái)說,我們想要在桌面端将一個組件當做浮層來(lái)渲染,但(dàn)在移動端則當作(zuò)行内組件。我們可以通過對(duì) <Teleport> 動态地傳入一個 disabled prop 來(lái)處理(lǐ)這(zhè)兩種不同情況。

template

<Teleport :disabled="isMobile">

...

</Teleport>

這(zhè)裏的 isMobile 狀态可以根據 CSS media query 的不同結果動态地更新。

多個 Teleport 共享目标 ​

一個可重用(yòng)的模态框組件可能(néng)同時(shí)存在多個實例。對(duì)于此類場景,多個 <Teleport> 組件可以将其内容挂載在同一個目标元素上(shàng),而順序就是簡單的順次追加,後挂載的将排在目标元素下(xià)更後面的位置上(shàng)。

比如下(xià)面這(zhè)樣的用(yòng)例:

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>

網站(zhàn)建設開(kāi)發|APP設計(jì)開(kāi)發|小(xiǎo)程序建設開(kāi)發
下(xià)一篇:Suspense
上(shàng)一篇:KeepAlive