要使组件成为异步边界,需让 setup 返回 promise(通过顶层 await 或 async setup),并由 suspense 直接包裹该组件,其 #default 插槽放组件、#fallback 插槽定义加载态;suspense 仅对直接子级生效,且等待所有异步子组件 resolve 后才切换。

必须用顶层 await 或 async setup 返回 Promise
Vue 仅在 setup 是异步函数(或含顶层 await)且尚未 resolve 时,才将该组件视为 pending 状态。写法示例如下:
<script setup>
// ✅ 正确:顶层 await,组件变成异步边界
const user = await fetch('/api/user').then(r => r.json())
// ✅ 也可写成 async setup() 形式(效果等价)
// export default {
// async setup() {
// const user = await fetch('/api/user').then(r => r.json())
// return { user }
// }
// }
</script>
Suspense 必须包裹该组件的直接调用位置
父组件中需用 <suspense></suspense> 包裹,并严格使用 #default 和 #fallback 插槽:
<template><suspense><template><userprofile></userprofile><!-- UserProfile 含 async setup --></template><template><div class="skeleton">加载用户信息中…</div>
</template></suspense></template>
注意几个易错点
- 异步组件必须是
<suspense></suspense>的直接子级,不能套在 div、template 或其它组件里,否则 Suspense 无法检测 - 多个
async setup子组件同时存在时,Suspense 会等待全部 resolve 才切换到 default 插槽 - 不建议在顶层 await 中做耗时过长或可能失败的请求;错误不会自动被捕获,需配合
onErrorCaptured或 try/catch + 状态兜底 - API 请求逻辑更适合放在
onMounted中配合 loading state 控制,除非你明确需要组件级挂起(比如路由级首屏数据强依赖)
fallback 显示时机很精准
从父组件 render 开始,到子组件 setup Promise resolve 完成前,Suspense 一直渲染 fallback;resolve 后立即 diff 并挂载真实 DOM,整个过程不闪跳、不重绘占位元素。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











