Teleport / Suspense / 动态组件
约 752 字大约 3 分钟
布欧-Lewyon
2026-05-15
首页 › Vue › 进阶特性 › Teleport / Suspense / 动态组件
Teleport
<Teleport> 将子元素渲染到 DOM 树的其他位置,而非当前组件树中。解决模态框、通知等需要"跳出"父级 overflow:hidden / z-index 层叠上下文的场景。
<template>
<button @click="open = true">打开弹窗</button>
<!-- 将弹窗渲染到 <body> 中 -->
<Teleport to="body">
<div v-if="open" class="modal">
<p>我是弹窗内容</p>
<button @click="open = false">关闭</button>
</div>
</Teleport>
</template>多个 Teleport 到同一目标
<template>
<Teleport to="#modals">
<ModalA />
</Teleport>
<Teleport to="#modals">
<ModalB /> <!-- 追加到 #modals 中 -->
</Teleport>
</template>条件禁用
<Teleport :to="isMobile ? 'body' : ''">
<!-- isMobile 时 Teleport 到 body,否则原地渲染 -->
</Teleport>与 CSS 动画配合
<template>
<Teleport to="body">
<Transition name="fade">
<div v-if="show" class="modal-backdrop">
<div class="modal-content">
<slot />
</div>
</div>
</Transition>
</Teleport>
</template>Suspense
<Suspense> 管理异步组件加载过程中的等待状态。
异步组件
<script setup>
import { defineAsyncComponent } from 'vue'
// 懒加载组件
const AsyncUserList = defineAsyncComponent(() =>
import('./UserList.vue')
)
</script>
<template>
<Suspense>
<!-- 异步组件加载时要显示的默认内容 -->
<AsyncUserList />
<!-- fallback:加载过程中显示 -->
<template #fallback>
<div class="loading">加载中...</div>
</template>
</Suspense>
</template>Suspense 中的异步 setup
Suspense 中的组件可以在 setup 中使用 await:
<!-- UserList.vue -->
<script setup>
const users = await fetch('/api/users').then(r => r.json())
// ↑ setup 顶层 await → 触发 Suspense fallback
</script>
<template>
<ul>
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>
</template>自动清理副作用
Suspense 会在组件卸载时自动清理未完成的异步操作:
<script setup>
const data = await fetch('/api/data')
// 如果组件在请求完成前被 Suspense 替换 → 请求自动取消
</script>嵌套 Suspense
<template>
<Suspense>
<div>
<Suspense>
<AsyncA />
<template #fallback>loading A...</template>
</Suspense>
<Suspense>
<AsyncB />
<template #fallback>loading B...</template>
</Suspense>
</div>
<template #fallback>
loading 主内容...
</template>
</Suspense>
</template>动态组件
<component :is>
<script setup>
import { shallowRef } from 'vue'
import TabA from './TabA.vue'
import TabB from './TabB.vue'
import TabC from './TabC.vue'
const currentTab = shallowRef(TabA)
const tabs = { TabA, TabB, TabC }
</script>
<template>
<button v-for="(_, name) in tabs" :key="name"
@click="currentTab = tabs[name]">
{{ name }}
</button>
<!-- 动态渲染当前选中的组件 -->
<component :is="currentTab" />
</template>keep-alive 缓存
<template>
<!-- 切换 tab 时,保持组件状态不被销毁 -->
<KeepAlive>
<component :is="currentTab" />
</KeepAlive>
</template>
KeepAlive配合动态组件使用,缓存组件实例。被缓存的组件会触发onActivated/onDeactivated钩子。
综合示例:可配置的弹出层系统
<!-- NotificationCenter.vue -->
<script setup>
import { ref } from 'vue'
const notifications = ref([
{ id: 1, text: '保存成功', type: 'success' },
{ id: 2, text: '网络错误', type: 'error' }
])
</script>
<template>
<Teleport to="body">
<div class="notification-container">
<TransitionGroup name="slide">
<div v-for="n in notifications" :key="n.id"
:class="['notification', n.type]">
{{ n.text }}
</div>
</TransitionGroup>
</div>
</Teleport>
</template>小结
| 特性 | 作用 | 典型场景 |
|---|---|---|
| Teleport | 渲染到指定 DOM 节点 | 模态框、通知、工具提示 |
| Suspense | 异步组件加载状态管理 | 懒加载组件、异步 setup |
| 动态组件 | 动态切换渲染的组件 | Tab 切换、表单步骤 |
| KeepAlive | 缓存组件实例避免销毁重建 | 频繁切换的 tab、缓存表单状态 |
上一节:自定义指令 下一节:Vue Router 基础
