Teleport
<Teleport> je vestavěná komponenta, který nám umožňuje „teleportovat“ část šablony komponenty do DOM elementu, který existuje mimo DOM hierarchii této komponenty.
Základní použití
Někdy část šablony logicky patří ke komponentě, ale z vizuálního hlediska by měla být zobrazena jinde v DOM, možná dokonce mimo celou Vue aplikaci.
Nejběžnějším příkladem je vytváření modálního okna přes celou obrazovku. Ideálně bychom chtěli, aby byl kód pro tlačítko zobrazení okna a pro modální okno samotné napsán uvnitř stejné Single-File komponenty (SFC), protože obě části souvisí se stavem otevření / zavření modálního okna. Ale to znamená, že modální okno bude vykresleno spolu s tlačítkem, hluboce vnořeno v DOM hierarchii aplikace. To může přinést různé záludné problémy při pozicování modálního okna pomocí CSS.
Představte si následující HTML strukturu:
template
<div class="outer">
<h3>Příklad na Vue Teleport</h3>
<div>
<MyModal />
</div>
</div>A zde je implementace komponenty <MyModal>:
vue
<script setup>
import { ref } from 'vue'
const open = ref(false)
</script>
<template>
<button @click="open = true">Otevřít modální okno</button>
<div v-if="open" class="modal">
<p>Ahoj z modálního okna!</p>
<button @click="open = false">Zavřít</button>
</div>
</template>
<style scoped>
.modal {
position: fixed;
z-index: 999;
top: 20%;
left: 50%;
width: 300px;
margin-left: -150px;
}
</style>Komponenta obsahuje <button>, který vyvolá otevření modálního okna, a element <div> s třídou .modal, který obklopuje samotný obsah modálního okna a tlačítko pro jeho uzavření.
Při použití této komponenty uvnitř původní HTML struktury může nastat několik potenciálních problémů:
position: fixedumisťuje prvek relativně k oknu prohlížeče pouze tehdy, když žádný nadřazený prvek nemá nastavenou vlastnosttransform,perspectivenebofilter. Pokud například chceme animovat nadřazený prvek<div class="outer">pomocí CSS transformace, způsobí to narušení layoutu modálního okna!z-indexmodálního okna je omezen na jeho nadřazené prvky. Pokud existuje jiný prvek, který překrývá<div class="outer">a má vyššíz-index, překryje náš modální dialog.
<Teleport> poskytuje čistý způsob, jak tyto problémy obejít, umožňuje nám vymanit se z vnořené DOM struktury. Upravme komponentu <MyModal>, aby používala <Teleport>:
template
<button @click="open = true">Otevřít modální okno</button>
<Teleport to="body">
<div v-if="open" class="modal">
<p>Ahoj z modálního okna!</p>
<button @click="open = false">Zavřít</button>
</div>
</Teleport>Atribut to uvnitř <Teleport> slouží jako cíl a očekává CSS selektor nebo samotný DOM element. Zde v podstatě říkáme Vue, ať „teleportuje tento fragment šablony do tagu body“.
Můžete kliknout na tlačítko níže a pomocí nástrojů pro vývojáře ve vašem prohlížeči zkontrolovat tag <body>:
Pro vytvoření animovaných modálních oken můžete <Teleport> kombinovat s <Transition> - viz příklad zde.
TIP
Cíl teleportace to už musí být v DOM, když je komponenta <Teleport> připojena (mounted). Ideálně by to měl být prvek mimo celou Vue aplikaci. Pokud je cílem teleportace jiný prvek vykreslený Vue, musíte se ujistit, že je tento prvek připojen dříve než <Teleport>.
Použití s komponentami
<Teleport> upravuje pouze vykreslenou strukturu DOM. Neovlivňuje logickou hierarchii komponent. To znamená, že pokud <Teleport> obsahuje komponentu, tato komponenta zůstane logickým potomkem rodičovské komponenty obsahující <Teleport>. Předávání vlastností (props) a emitování událostí (emits) bude fungovat stále stejným způsobem.
To také znamená, že inject z rodičovské komponenty funguje podle očekávání a že komponenta potomka bude pod komponentu rodiče vnořena i ve Vue Devtools, místo aby byla umístěna tam, kam se přesunul výsledný obsah.
Zakázání teleportace
V některých případech můžeme chtít <Teleport> podmíněně zakázat. Například můžeme chtít vykreslit komponentu jako overlay pro desktop, ale inline na mobilu. <Teleport> podporuje vlastnost disabled, která může být dynamicky přepínána:
template
<Teleport :disabled="isMobile">
...
</Teleport>Poté můžeme hodnotu isMobile dynamicky aktualizovat.
Více teleportací na stejný cíl
Běžným použitím by byla znovupoužitelná komponenta <Modal>, která může mít současně více aktivních instancí. Pro tento případ může více komponent <Teleport> připojit svůj obsah ke stejnému cílovému elementu. Pořadí bude jednoduché připojení na konec (append). Později připojené fragmenty šablony budou uvnitř téhož cílového elementu umístěny za dřívějšími.
S následujícím použitím:
template
<Teleport to="#modals">
<div>A</div>
</Teleport>
<Teleport to="#modals">
<div>B</div>
</Teleport>Bude vykresleným výsledkem:
html
<div id="modals">
<div>A</div>
<div>B</div>
</div>Odložený Teleport
Ve Vue 3.5+ můžeme použít vlastnost defer pro odložení vyhodnocení cíle Teleportu, dokud nebudou připojeny (mounted) další části aplikace. To umožní Teleportu cílit na kontejner, který je také vykreslován Vue, ale až v pozdější části stromu komponent:
template
<Teleport defer to="#late-div">...</Teleport>
<!-- někdy později v šabloně -->
<div id="late-div"></div>Pamatujte, že cílový element musí být vykreslen v stejném mount / update cyklu jako Teleport – např. pokud je <div> vykreslen pouze o vteřinu později, Teleport stejně ohlásí chybu. Odložení funguje stejně jako lifecycle hook mounted.
Související