suspense 在 vue 3 中通过声明式包裹与异步机制自动协调加载生命周期,需配合 defineasynccomponent 或顶层 await 才生效;必须使用 #default 和 #fallback 双插槽,缺一不可。

Suspense 在 Vue 3 单文件组件中不是用来“手动控制 loading 开关”的工具,而是通过声明式包裹 + 异步执行机制,自动协调子组件的加载生命周期。关键前提是:子组件必须真正“异步”,否则 Suspense 不会触发 fallback。
必须用 defineAsyncComponent 引入组件
静态 import 的组件会同步解析,Suspense 完全感知不到异步过程,直接渲染 default 插槽,fallback 永远不会出现。只有用 defineAsyncComponent 才能激活 Suspense 的等待逻辑:
- 正确写法:
const AsyncUserCard = defineAsyncComponent(() => import('./UserCard.vue')) - 错误写法:
import AsyncUserCard from './UserCard.vue'(这会让整个父组件等它编译完才挂载)
在模板中用双插槽结构包裹
Suspense 必须有且仅有两个命名插槽:#default 放异步组件实例,#fallback 放加载态 UI。不能省略任一插槽,也不能用 v-if/v-show 替代:
- ✅ 正确:
<template><asyncusercard></asyncusercard></template> - ✅ 正确:
<template><spinner></spinner></template> - ❌ 错误:
<suspense v-if="loading">...</suspense>(破坏了 Suspense 自动状态机)
可配合 setup 中的顶层 await 处理数据请求
如果子组件内部用 顶层 await(比如 const data = await fetch(...)),Suspense 同样会等待该 Promise 完成。此时子组件无需 defineAsyncComponent,但必须满足两个条件:
- 子组件使用
<script setup lang="ts"></script>语法 - await 表达式位于 script 顶层(不在函数内),且返回值用于模板响应式渲染
- 例如:
const user = await $fetch('/api/user'),后续模板中直接用{{ user.name }}
多个异步内容可自然聚合
一个 Suspense 可同时等待多个异步来源:比如子组件是 defineAsyncComponent,且其内部又有顶层 await 请求;或者多个并列的异步组件被同一 Suspense 包裹。Suspense 会等到所有 Promise 都 resolve 后才切换到 default 插槽,避免内容闪烁或局部加载完成就渲染的错乱。
不复杂但容易忽略。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










