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 se můžeme setkat s tímto scénářem: část šablony komponenty k ní logicky patří, ale z vizuálního hlediska by měla být zobrazena jinde v DOM, mimo 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 tlačítko modálního okna a samotné modální okno existovaly uvnitř stejné komponenty, protože obě 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: fixed
umisťuje prvek relativně k oknu prohlížeče pouze tehdy, když žádný nadřazený prvek nemá nastavenou vlastnosttransform
,perspective
nebofilter
. 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-index
modá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>
Stav isMobile
může být dynamicky aktualizován pomocí detekce změn v media query.
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ř 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í