Vue3 Teleport 与 Suspense
Teleport
Teleport 允许我们将组件的 HTML 渲染到 DOM 中的任意位置。
基本用法
<template>
<button @click="open = true">打开模态框</button>
<Teleport to="body">
<div v-if="open" class="modal">
<p>这是一个模态框</p>
<button @click="open = false">关闭</button>
</div>
</Teleport>
</template>
<script setup>
import { ref } from 'vue'
const open = ref(false)
</script>
禁用 Teleport
<Teleport :disabled="isMobile" to="body">
<div>内容</div>
</Teleport>
多个 Teleport
<Teleport to="#modals">
<div>Modal 1</div>
</Teleport>
<Teleport to="#modals">
<div>Modal 2</div>
</Teleport>
实际应用:模态框组件
<!-- Modal.vue -->
<template>
<Teleport to="body">
<Transition name="modal">
<div v-if="show" class="modal-mask" @click="$emit('close')">
<div class="modal-container" @click.stop>
<div class="modal-header">
<slot name="header">默认标题</slot>
</div>
<div class="modal-body">
<slot></slot>
</div>
<div class="modal-footer">
<slot name="footer">
<button @click="$emit('close')">关闭</button>
</slot>
</div>
</div>
</div>
</Transition>
</Teleport>
</template>
<script setup>
defineProps({
show: Boolean
})
defineEmits(['close'])
</script>
<style scoped>
.modal-mask {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
}
.modal-container {
background: white;
border-radius: 8px;
padding: 20px;
max-width: 500px;
}
</style>
Suspense
Suspense 用于协调异步依赖的处理。
基本用法
<template>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
</template>
<script setup>
import AsyncComponent from './AsyncComponent.vue'
</script>
异步组件
<!-- AsyncComponent.vue -->
<script setup>
const data = await fetch('/api/data').then(r => r.json())
</script>
<template>
<div>{{ data }}</div>
</template>
异步 setup
<script setup>
import { ref } from 'vue'
const data = ref(null)
// 顶层 await
data.value = await fetch('/api/data').then(r => r.json())
</script>
错误处理
<template>
<Suspense @pending="onPending" @resolve="onResolve" @fallback="onFallback">
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
</template>
<script setup>
import { onErrorCaptured } from 'vue'
const onPending = () => console.log('pending')
const onResolve = () => console.log('resolved')
const onFallback = () => console.log('fallback')
onErrorCaptured((err) => {
console.error('Error:', err)
return false
})
</script>
嵌套 Suspense
<template>
<Suspense>
<template #default>
<ParentComponent>
<Suspense>
<template #default>
<ChildComponent />
</template>
<template #fallback>
<div>子组件加载中...</div>
</template>
</Suspense>
</ParentComponent>
</template>
<template #fallback>
<div>父组件加载中...</div>
</template>
</Suspense>
</template>
实际应用
<template>
<Suspense>
<template #default>
<UserProfile :id="userId" />
</template>
<template #fallback>
<div class="loading">
<Spinner />
<p>加载用户信息...</p>
</div>
</template>
</Suspense>
</template>
<script setup>
import { ref } from 'vue'
import UserProfile from './UserProfile.vue'
import Spinner from './Spinner.vue'
const userId = ref(1)
</script>