Suspense 是 Vue 3 引入的一个内置组件,不需要引用可以直接用。用于处理异步依赖的等待状态。虽然 Suspense 主要用于异步组件,但你也可以全局地使用它来管理整个应用的加载状态。
全局 Suspense 的基本用法
1. 在根组件中使用 Suspense
// main.js 或 main.ts
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
<!-- App.vue -->
<template>
<Suspense>
<router-view />
<template #fallback>
<div class="loading-indicator">加载中...</div>
</template>
</Suspense>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
.loading-indicator {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-size: 1.5rem;
}
</style>
2. 结合异步组件使用
// 路由配置示例 (router.js)
import { createRouter, createWebHistory } from 'vue-router'
const Home = () => ({
component: import('./views/Home.vue'),
loading: LoadingComponent, // 可选
error: ErrorComponent, // 可选
delay: 200, // 延迟显示加载组件
timeout: 3000 // 超时时间
})
const routes = [
{ path: '/', component: Home }
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
高级全局 Suspense 实现
1. 创建全局加载状态管理
javascript
// stores/loading.js (使用Pinia)
import { defineStore } from 'pinia'
export const useLoadingStore = defineStore('loading', {
state: () => ({
isLoading: false,
message: '加载中...'
}),
actions: {
startLoading(message) {
this.isLoading = true
this.message = message || '加载中...'
},
stopLoading() {
this.isLoading = false
}
}
})
2. 创建全局 Suspense 组件
vue
<!-- components/GlobalSuspense.vue -->
<template>
<Suspense @pending="onPending" @resolve="onResolve" @fallback="onFallback">
<slot />
<template #fallback>
<div v-if="isLoading" class="global-loading">
<div class="spinner"></div>
<p>{{ message }}</p>
</div>
</template>
</Suspense>
</template>
<script setup>
import { useLoadingStore } from '@/stores/loading'
const loadingStore = useLoadingStore()
const { isLoading, message } = storeToRefs(loadingStore)
const onPending = () => {
loadingStore.startLoading()
}
const onResolve = () => {
loadingStore.stopLoading()
}
const onFallback = () => {
// 可以添加额外的回调逻辑
}
</script>
<style>
.global-loading {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.8);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 9999;
}
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
3. 在应用中使用全局 Suspense
vue
<!-- App.vue -->
<template>
<GlobalSuspense>
<router-view />
</GlobalSuspense>
</template>
<script setup>
import GlobalSuspense from '@/components/GlobalSuspense.vue'
</script>
注意事项
错误处理:Suspense 本身不处理错误,需要使用
onErrorCaptured
或errorCaptured
钩子嵌套 Suspense:可以嵌套使用 Suspense,内层 Suspense 会优先于外层
SSR 兼容:在服务端渲染时 Suspense 行为有所不同
组合式 API:在 setup 中使用 async 时,组件会自动成为 Suspense 的异步依赖
错误处理示例
vue
<template>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
</template>
<script setup>
import { onErrorCaptured } from 'vue'
import AsyncComponent from './AsyncComponent.vue'
onErrorCaptured((error) => {
console.error('Error caught by Suspense:', error)
// 可以在这里显示错误界面
return false // 阻止错误继续向上传播
})
</script>
通过这种方式,你可以在整个 Vue 3 应用中实现统一的加载状态管理和优雅的异步处理体验。