Vue 3 中设置异步组件骨架屏的最推荐方式是用 Suspense 包裹由 defineAsyncComponent 创建的异步组件,#fallback 插槽放结构匹配的真实骨架屏,不手动管理 loading 状态。

Vue 3 中设置异步组件的加载骨架屏,核心是用 Suspense 组件配合 defineAsyncComponent,把骨架屏直接放在 #fallback 插槽里——不用手动维护 loading 状态,也不需要 watch 或 v-if 切换。
用 Suspense 包裹异步组件
这是最推荐、最简洁的方式。Suspense 会自动监听内部异步操作(比如 defineAsyncComponent 返回的 Promise),并在等待期间渲染 fallback 内容。
- 异步组件必须由
defineAsyncComponent创建,且返回一个原生 Promise(例如() => import('./MyPage.vue')) - 不能写成
async () => { return await import(...) },否则 Suspense 无法捕获异步流程 -
#default放真实组件,#fallback放骨架屏组件(如<skeletoncard></skeletoncard>或<el-skeleton></el-skeleton>)
骨架屏要匹配真实内容结构
骨架屏不是装饰,而是布局占位器。它应和最终内容保持一致的容器尺寸、行高、间距和区块数量。
从零开始编排 SpringBoot+Vue 全栈项目工作流,适用于新建全栈项目或完整脚手架请求,触发后依次调用 product‑spec‑builder(需求调研)等模块。
- 例如列表页有 3 行卡片,骨架屏就画 3 个等高的灰色卡片块
- 头像位置用
variant="circle",标题用variant="text"并设对应宽度,避免布局跳动 - 不要在 fallback 里发起 API 请求或使用复杂逻辑,保持轻量,只做视觉占位
细粒度控制:单个列表项用独立 Suspense
如果整个页面用一个 Suspense,可能造成“全屏卡顿”。对长列表或卡片流,建议每个子项单独封装异步逻辑:
立即学习“前端免费学习笔记(深入)”;
- 把每张卡片做成一个 defineAsyncComponent(如
ItemCardAsync) - 在循环中为每一项包裹自己的
<suspense></suspense>,各自管理加载状态 - 这样用户能看到部分卡片先出现,其余仍在加载,体验更自然
避免常见错误
有些写法看似可行,实则破坏 Suspense 的机制或导致骨架屏失效:
- 在 setup 中直接读
$slots判断插槽是否就绪——不可靠,插槽是运行时动态挂载的 - 用
v-if="!loaded"手动控制骨架屏显隐——绕过了 Suspense 的状态聚合能力 - 在骨架屏组件内部调用
onMounted去请求数据——违背“占位”原则,也容易引发竞态 - 把骨架屏写在异步组件内部(比如 UserProfile.vue 里自己处理 loading)——职责错位,应由 Suspense 统一协调

















