Skip to content

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>
vue
<script> export default {   data() {     return {       open: 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 vlastnost transform, perspective nebo filter. 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>

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í

Teleport has loaded