Suspense 是 Vue 3 内置组件,通过 #default 和 #fallback 两个具名插槽声明式管理异步加载,要求 fallback 为静态骨架屏、异步组件须用 defineAsyncComponent 包装,并自动聚合整棵组件树的异步依赖。

怎么写一个有效的 Suspense 结构
必须使用两个具名插槽,缺一不可:
-
#default:放真实内容,比如异步组件、含
async setup()的组件,或依赖异步数据的子组件 -
#fallback:只放轻量、静态的占位 UI,不能含响应式逻辑(如
v-if、ref、动态 class),推荐用纯 CSS 骨架屏(灰色块 + 渐变背景)
错误写法示例:<template #fallback><Loading :visible="loading" /></template> —— 这里 :visible 是响应式绑定,会触发重渲染,破坏 Suspense 的状态稳定性。
异步组件必须用 defineAsyncComponent 包装
直接在 setup 中 await import() 会阻塞组件初始化,违背 Suspense 设计初衷。正确方式是:
- 基础用法:
const AsyncPage = defineAsyncComponent(() => import('./Page.vue')) - 加超时控制:
defineAsyncComponent({ loader: () => import('./Page.vue'), timeout: 5000 }),超时后抛出错误,可被上层捕获 - 配合路由懒加载时,
component: () => import('./Page.vue')本质也是 defineAsyncComponent 的简写,同样受 Suspense 管理
骨架屏要“结构一致”,不是随便画个 loading
切换时无跳动的关键,在于 fallback 和真实内容 DOM 结构、尺寸比例尽量对齐:
立即学习“前端免费学习笔记(深入)”;
- 列表页有 6 条数据?fallback 就画 6 个等高灰色条,高度用
rem或固定像素,别用min-height - 用户卡片含头像+昵称+简介三部分?fallback 也保留三块区域,头像用圆角灰色块,文字行用横向灰色条模拟
- 避免图片、SVG、复杂动画——它们体积大、渲染慢,反而拖慢 fallback 显示速度
Suspense 会自动聚合整棵树的异步依赖
它不止等组件加载,还会捕获子组件内部的异步行为:
-
AsyncProfile组件中用了async setup()请求用户信息 → Suspense 会等组件加载完 + 接口返回后才切换 - 多个并列异步组件(如侧边栏 + 主内容区)→ 不用 Promise.all,Suspense 自动合并所有 pending 状态
- 嵌套 Suspense 也支持:父级等待整体就绪,子级可细化控制局部加载态(比如弹窗内单独 fallback)
这种聚合能力让状态逻辑彻底从组件中解耦,不再需要层层透传 loading 标志或手动同步 promise 生命周期。


















