Vue 3 Teleport i Suspense: Zaawansowane Wzorce i Pytania Rekrutacyjne 2026
Kompleksowy przewodnik po Vue 3 Teleport i Suspense - zaawansowane wzorce renderowania, obsługa błędów oraz najczęstsze pytania na rozmowach kwalifikacyjnych dla seniorów Vue.

Vue 3 Teleport i Suspense rozwiązują dwa odrębne problemy renderowania: umieszczanie komponentów poza ich hierarchią DOM oraz koordynowanie asynchronicznych zależności. Oba są wbudowanymi komponentami niewymagającymi importów i oba pojawiają się regularnie na rozmowach kwalifikacyjnych dla seniorów Vue.
Vue 3.5 dodał prop defer do Teleport, umożliwiając cele renderowane później w drzewie komponentów. To rozwiązuje częsty problem SSR, gdy cel portalu nie istnieje w momencie montowania.
Podstawy Teleport i Prop defer
Teleport przenosi wyrenderowaną zawartość do innej lokalizacji DOM, zachowując jednocześnie drzewo komponentów Vue. Komponent pozostaje logicznym dzieckiem swojego rodzica: propsy, zdarzenia i provide/inject działają normalnie. Vue DevTools pokazuje przeteleportowany komponent zagnieżdżony pod jego rodzicem, nie pod elementem docelowym.
<!-- ModalTrigger.vue -->
<script setup>
import { ref } from 'vue'
const isOpen = ref(false)
</script>
<template>
<button @click="isOpen = true">Open Modal</button>
<Teleport to="#modal-root">
<div v-if="isOpen" class="modal-overlay" @click.self="isOpen = false">
<div class="modal-content">
<slot />
<button @click="isOpen = false">Close</button>
</div>
</div>
</Teleport>
</template>Prop to przyjmuje string z selektorem CSS lub referencję do elementu DOM. Gdy cel nie istnieje w momencie montowania, Teleport wyświetla ostrzeżenie i nie renderuje niczego. To tworzy problemy z SSR i dynamicznymi layoutami.
Prop defer (Vue 3.5+)
Prop defer informuje Teleport, aby poczekał, aż inne części aplikacji zostaną zamontowane przed rozwiązaniem celu. To umożliwia teleportację do elementów renderowanych później w tym samym komponencie lub w komponentach-rodzeństwie.
<!-- App.vue -->
<template>
<Teleport defer to="#dynamic-target">
<NotificationBanner />
</Teleport>
<!-- Target rendered after the Teleport -->
<div id="dynamic-target"></div>
</template>Bez defer ten kod zawiedzie, ponieważ #dynamic-target nie istnieje gdy Teleport się montuje. Z defer, Vue rozwiązuje cel po tym jak wszystkie komponenty w tym samym cyklu zostały zamontowane. Cel musi nadal renderować się w tym samym cyklu montowania lub aktualizacji.
Wiele Teleportów i Renderowanie Warunkowe
Wiele komponentów Teleport może celować w ten sam element. Zawartość dodawana jest w kolejności deklaracji:
<template>
<Teleport to="#notifications">
<Toast message="First" />
</Teleport>
<Teleport to="#notifications">
<Toast message="Second" />
</Teleport>
</template>
<!-- Result in #notifications:
<div>First</div>
<div>Second</div>
-->Prop disabled kontroluje czy teleportacja ma miejsce. Gdy wyłączony, zawartość renderuje się inline. Ten wzorzec obsługuje responsywne layouty, gdzie modale powinny nakładać się na desktopie, ale renderować inline na urządzeniach mobilnych:
<script setup>
import { useMediaQuery } from '@vueuse/core'
const isMobile = useMediaQuery('(max-width: 768px)')
</script>
<template>
<Teleport to="body" :disabled="isMobile">
<MobileDrawer />
</Teleport>
</template>Suspense dla Orkiestracji Komponentów Asynchronicznych
Suspense koordynuje asynchroniczne zależności w drzewie komponentów. Gdy jakikolwiek komponent potomny ma nierozwiązaną zależność asynchroniczną, Suspense wyświetla zawartość slotu fallback. Po rozwiązaniu wszystkich zależności, Suspense przełącza się na slot domyślny.
Suspense pozostaje eksperymentalny w Vue 3.5. API może zmienić się przed osiągnięciem stabilnego statusu. Użycie produkcyjne wymaga akceptacji tego ryzyka.
<!-- Dashboard.vue -->
<template>
<Suspense>
<template #default>
<DashboardContent />
</template>
<template #fallback>
<LoadingSpinner />
</template>
</Suspense>
</template>Suspense śledzi dwa typy zależności asynchronicznych: komponenty z async setup() oraz komponenty asynchroniczne utworzone przez defineAsyncComponent.
Async Setup z Top-Level Await
Vue 3 script setup wspiera top-level await. Każdy komponent używający top-level await staje się zależnością asynchroniczną, którą Suspense może śledzić:
<!-- UserProfile.vue -->
<script setup>
const response = await fetch('/api/user/profile')
const user = await response.json()
</script>
<template>
<div class="profile">
<h1>{{ user.name }}</h1>
<p>{{ user.email }}</p>
</div>
</template>Ten komponent nie może się wyrenderować dopóki fetch nie zakończy się. Nadrzędna granica Suspense wyświetla fallback do momentu rozwiązania. Bez granicy Suspense, komponent po prostu się nie renderuje.
Zagnieżdżony Suspense z Propem suspensible
Złożone aplikacje często mają wiele granic asynchronicznych. Vue 3.3 wprowadził prop suspensible do kontrolowania interakcji zagnieżdżonych komponentów Suspense z ich rodzicami.
<!-- PageLayout.vue -->
<template>
<Suspense>
<template #default>
<header>
<AsyncNavigation />
</header>
<main>
<!-- Inner Suspense with its own fallback -->
<Suspense suspensible>
<template #default>
<AsyncContent />
</template>
<template #fallback>
<ContentSkeleton />
</template>
</Suspense>
</main>
</template>
<template #fallback>
<PageSkeleton />
</template>
</Suspense>
</template>Gdy suspensible jest ustawiony, wewnętrzny Suspense rejestruje się u nadrzędnej granicy Suspense. Rodzic czeka na rozwiązanie zarówno AsyncNavigation jak i AsyncContent przed ukryciem swojego fallbacku. Wewnętrzny Suspense nadal służy jako lokalna granica: jeśli AsyncContent trwa dłużej, ContentSkeleton wyświetla się podczas gdy AsyncNavigation pozostaje widoczna.
Bez suspensible, wewnętrzny Suspense działa niezależnie. Rodzic pokazuje swój fallback tylko podczas ładowania AsyncNavigation, całkowicie ignorując AsyncContent.
Gotowy na rozmowy o Vue.js / Nuxt.js?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Łączenie Teleport, Suspense i Transitions
Rzeczywiste aplikacje łączą te wzorce. Modal ładujący zawartość asynchronicznie korzysta ze wszystkich trzech:
<!-- AsyncModal.vue -->
<script setup>
import { ref, defineAsyncComponent } from 'vue'
const isOpen = ref(false)
const AsyncModalContent = defineAsyncComponent({
loader: () => import('./ModalContent.vue'),
loadingComponent: () => import('./ModalSkeleton.vue'),
delay: 200,
timeout: 10000
})
</script>
<template>
<button @click="isOpen = true">Open</button>
<Teleport to="body">
<Transition name="modal">
<div v-if="isOpen" class="modal-wrapper">
<Suspense>
<template #default>
<AsyncModalContent @close="isOpen = false" />
</template>
<template #fallback>
<ModalSkeleton />
</template>
</Suspense>
</div>
</Transition>
</Teleport>
</template>
<style>
.modal-enter-active,
.modal-leave-active {
transition: opacity 0.3s ease;
}
.modal-enter-from,
.modal-leave-to {
opacity: 0;
}
</style>Kolejność zagnieżdżenia ma znaczenie: Teleport opakowuje Transition, który opakowuje element warunkowy, który zawiera Suspense. To zapewnia, że przejście animuje cały modal włącznie ze stanami ładowania.
Obsługa Błędów z Suspense
Suspense nie obsługuje błędów. Nieudane zależności asynchroniczne odrzucają swoje obietnice i propagują błędy w górę drzewa komponentów. Hook cyklu życia onErrorCaptured przechwytuje te błędy na granicy:
<!-- ErrorBoundary.vue -->
<script setup>
import { ref, onErrorCaptured } from 'vue'
const error = ref(null)
onErrorCaptured((err) => {
error.value = err
return false // Prevent propagation
})
</script>
<template>
<div v-if="error" class="error-state">
<p>Failed to load: {{ error.message }}</p>
<button @click="error = null">Retry</button>
</div>
<Suspense v-else>
<template #default>
<slot />
</template>
<template #fallback>
<slot name="loading" />
</template>
</Suspense>
</template>Ten wzorzec opakowuje każde drzewo asynchronicznych komponentów zarówno stanami ładowania jak i błędów. Przycisk ponowienia czyści błąd, powodując ponowne renderowanie Vue i ponowną próbę operacji asynchronicznej.
Pytania Rekrutacyjne o Teleport i Suspense
Stanowiska seniorów Vue coraz częściej testują te wzorce. Typowe pytania i co wyróżnia mocne odpowiedzi:
P: Kiedy należy użyć Teleport zamiast pozycjonowania CSS?
Słaba odpowiedź: "Gdy z-index nie działa."
Mocna odpowiedź: Teleport rozwiązuje problemy hierarchii DOM, których CSS nie może. Modal wewnątrz kontenera z overflow: hidden lub transform tworzy nowy kontekst warstw, psując position: fixed. Teleport przenosi modal do body, uciekając od tych ograniczeń przy zachowaniu logicznego drzewa komponentów Vue.
P: Jak Suspense różni się od stanów ładowania w każdym komponencie?
Słaba odpowiedź: "Suspense jest czystszy."
Mocna odpowiedź: Suspense koordynuje wiele zależności asynchronicznych. Dziesięć komponentów z indywidualnymi stanami ładowania błyska dziesięcioma spinnerami w różnym czasie. Suspense pokazuje jeden spinner dopóki wszystkie dziesięć nie zostaną rozwiązane, potem ujawnia je razem. To tworzy płynniejszą postrzeganą wydajność i prostszy kod komponentów, ponieważ komponenty asynchroniczne nie muszą zarządzać własnymi stanami ładowania.
P: Co się dzieje gdy cel Teleport nie istnieje?
Zawartość nie renderuje się i Vue loguje ostrzeżenie. Prop defer w Vue 3.5 rozwiązuje to, czekając aż inne komponenty najpierw się zamontują. Bez defer, należy upewnić się że cele istnieją w index.html lub w komponencie nadrzędnym, który montuje się przed dziećmi.
P: Czy Suspense działa z Options API?
Tak. Każdy komponent z async setup() uruchamia Suspense, niezależnie od tego czy używa Composition API czy Options API gdzie indziej. Funkcja setup jest granicą asynchroniczną, nie styl komponentu.
Teleport i Suspense w Nuxt 3
Nuxt 3 rozszerza te wzorce o rozważania SSR. Dokumentacja Nuxt o SSR opisuje timing hydracji.
Dla Teleport, Nuxt wymaga aby cel istniał przed hydracją. Należy dodać cele portali do app.vue lub użyć prop defer:
<!-- app.vue -->
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
<!-- Portal targets for SSR -->
<div id="modal-root"></div>
<div id="toast-root"></div>
</template>Dla Suspense, composable useFetch i useAsyncData Nuxta integrują się automatycznie. Komponenty stron używające tych composables działają z wbudowanym Suspense Nuxta bez ręcznej konfiguracji. Zobacz moduł Vue composables SharpSkill dla powiązanego przygotowania do rozmów.
Wzorce Produkcyjne dla Vue 3.5
- Cele Teleport w index.html: Tworzenie stabilnych celów portali istniejących przed uruchomieniem jakiegokolwiek kodu Vue. To całkowicie unika problemów z timingiem.
- Granularne granice Suspense: Jeden Suspense na niezależną jednostkę ładowania. Dashboard z oddzielnymi widgetami korzysta z oddzielnych granic Suspense, więc szybkie widgety pojawiają się natychmiast.
- Granice błędów powyżej Suspense: Zawsze należy opakowywać Suspense obsługą błędów. Awarie sieci i błędy API nie mogą crashować całego drzewa.
- Timing przejść: Przy łączeniu Suspense z Transition, należy ustawić
mode="out-in"aby zapobiec nakładaniu się podczas zmian stanu.
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Znajdziesz błąd w Vue.js / Nuxt.js?
Prawdziwy fragment kodu, ukryty błąd, jedna próba dziennie. Bez konta, żeby spróbować.

Autor:
Anthony Fillion-MailletZałożyciel SharpSkill
Programista fullstack od ponad 10 lat. Prowadzi SharpSkill i odpowiada za wszystko, co się tu ukazuje.
Zaktualizowano 28 sierpnia 2026
Tagi
Udostępnij
Powiązane artykuły

Vue 3 z TypeScriptem w 2026: bezpieczne typowanie propsów, emitów i composables
Praktyczny przewodnik po typowaniu komponentów Vue 3 w TypeScript: generyczne defineProps, krotkowe defineEmits, typowane composables, defineModel oraz InjectionKey wraz z pytaniami rekrutacyjnymi.

Vue 3 Composition API: Kompletny przewodnik po reaktywności i kompozycji
Praktyczny przewodnik po Vue 3 Composition API. Ref, reactive, computed, watch i composables — wszystko, czego potrzeba do budowania wydajnych aplikacji Vue.

Vue 3 Script Setup i defineModel w 2026: Nowoczesna Składnia i Pytania Rekrutacyjne
Kompleksowy przewodnik po składni script setup w Vue 3 oraz makrze defineModel. Omówienie reactive props destructure, typowania TypeScript oraz pytań rekrutacyjnych.