Телепортація у Vue.js
Що таке Teleport?
Teleport — це вбудований компонент Vue 3, який дозволяє рендерити контент в будь-якому місці DOM, навіть поза кореневим елементом Vue додатку.
Це особливо корисно для модальних вікон, підказок, сповіщень та інших елементів, які повинні бути поза поточною ієрархією.
Основне використання
<template>
<div class="component">
<button @click="showModal = true">Відкрити модальне вікно</button>
<Teleport to="body">
<div v-if="showModal" class="modal">
<p>Я рендерюся в body!</p>
<button @click="showModal = false">Закрити</button>
</div>
</Teleport>
</div>
</template>
<script setup>
import { ref } from 'vue'
const showModal = ref(false)
</script>
<style scoped>
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
z-index: 1000;
}
</style>Контент <Teleport> буде рендеритися в <body>, а не всередині .component.
Селектори для to
Атрибут to приймає CSS селектор або DOM елемент:
CSS Селектор
<!-- За ID -->
<Teleport to="#modal-container">
<div>Контент</div>
</Teleport>
<!-- За класом -->
<Teleport to=".modal-wrapper">
<div>Контент</div>
</Teleport>
<!-- За тегом -->
<Teleport to="body">
<div>Контент</div>
</Teleport>DOM Елемент
<script setup>
import { ref, onMounted } from 'vue'
const targetElement = ref(null)
onMounted(() => {
targetElement.value = document.getElementById('custom-container')
})
</script>
<template>
<Teleport :to="targetElement" v-if="targetElement">
<div>Контент</div>
</Teleport>
</template>Вимкнення Teleport
Ви можете динамічно вмикати/вимикати телепортацію:
<template>
<Teleport to="body" :disabled="!isMobile">
<div class="modal">
На мобільному - в body, на десктопі - тут
</div>
</Teleport>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const isMobile = ref(false)
onMounted(() => {
isMobile.value = window.innerWidth < 768
})
</script>Кілька Teleport до однієї цілі
Ви можете телепортувати кілька компонентів в одне місце:
<!-- Component1.vue -->
<Teleport to="#notifications">
<div>Сповіщення 1</div>
</Teleport>
<!-- Component2.vue -->
<Teleport to="#notifications">
<div>Сповіщення 2</div>
</Teleport><!-- index.html -->
<div id="app"></div>
<div id="notifications"></div>Обидва сповіщення будуть рендеритися в #notifications у порядку монтування.
Практичні приклади
Модальне вікно
<!-- Modal.vue -->
<template>
<Teleport to="body">
<Transition name="modal">
<div v-if="isOpen" class="modal-overlay" @click="close">
<div class="modal-content" @click.stop>
<button class="modal-close" @click="close">×</button>
<slot />
</div>
</div>
</Transition>
</Teleport>
</template>
<script setup>
defineProps({
isOpen: Boolean
})
const emit = defineEmits(['close'])
function close() {
emit('close')
}
</script>
<style scoped>
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background: white;
padding: 20px;
border-radius: 8px;
max-width: 500px;
position: relative;
}
.modal-close {
position: absolute;
top: 10px;
right: 10px;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
}
</style>Використання:
<template>
<button @click="isModalOpen = true">Відкрити</button>
<Modal :is-open="isModalOpen" @close="isModalOpen = false">
<h2>Заголовок</h2>
<p>Контент модального вікна</p>
</Modal>
</template>
<script setup>
import { ref } from 'vue'
import Modal from './Modal.vue'
const isModalOpen = ref(false)
</script>Teleport vs CSS position: fixed
Без Teleport
<template>
<div class="parent" style="position: relative; overflow: hidden">
<div class="modal" style="position: fixed">
<!-- Може бути обрізане через overflow: hidden -->
</div>
</div>
</template>Проблеми:
- Може бути обрізане батьківським
overflow - Спадковує контекст
z-index - Важко керувати порядком накладання
З Teleport
<template>
<div class="parent" style="position: relative; overflow: hidden">
<Teleport to="body">
<div class="modal" style="position: fixed">
<!-- Завжди відображається правильно -->
</div>
</Teleport>
</div>
</template>Переваги:
- Незалежний від стилів батька
- Власний контекст накладання
- Легко керувати
z-index
Обмеження
Цільовий елемент повинен існувати
<!-- Неправильно -->
<Teleport to="#non-existent">
<div>Помилка!</div>
</Teleport>
<!-- Правильно -->
<Teleport to="body">
<div>Працює!</div>
</Teleport>Teleport не змінює логічну ієрархію
<script setup>
import { provide } from 'vue'
provide('theme', 'dark')
</script>
<template>
<Teleport to="body">
<!-- inject все ще працює! -->
<ChildComponent />
</Teleport>
</template>Пропси, події, provide/inject продовжують працювати, оскільки змінюється лише DOM, а не ієрархія компонентів.
Висновок
Teleport:
- Рендерить контент в будь-якому місці DOM
- Не змінює логічну ієрархію компонентів
- Корисний для модальних вікон, підказок, сповіщень
- Вирішує проблеми з переповненням та
z-index - Може бути динамічно вимкнений
- Підтримує кілька телепортацій до однієї цілі
На співбесідах:
Важливо вміти:
- Пояснити, що таке Teleport і чому він потрібен
- Показати приклади використання (модальні вікна, сповіщення)
- Описати проблеми, які вирішує Teleport
- Пояснити, що логічна ієрархія не змінюється
- Навести відмінності від CSS position: fixed
Контент
Що таке Teleport?Основне використанняСелектори для toCSS СелекторDOM ЕлементВимкнення TeleportКілька Teleport до однієї ціліПрактичні прикладиМодальне вікноTeleport vs CSS position: fixedБез TeleportЗ TeleportОбмеженняЦільовий елемент повинен існуватиTeleport не змінює логічну ієрархіюВисновок
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.