Vue Suspense 结合骨架屏需确保结构一致、粒度合理、事件精细及SSR兼容:骨架屏须复刻真实组件布局并统一单位;Suspense 应包裹最小异步区块,配合 defineAsyncComponent 或顶层 await;利用 @pending/@resolve/@error 实现超时提示与淡入动画;SSR 下服务端不渲染骨架,由客户端接管以避免水合错误。
Vue Suspense 结合骨架屏,不是加个 loading 动画那么简单,而是让页面结构“稳住”,让用户眼睛有落点、心里有预期。关键在于用声明式方式把异步行为和视觉反馈对齐,而不是手动 toggle 状态。
骨架屏不是随便画几个灰色方块,它得是真实组件的“影子”。比如用户卡片有头像、昵称、简介三块区域,骨架屏就得用对应宽高的 .skeleton-avatar、.skeleton-name、.skeleton-bio 占位,保留 margin/padding/行高,否则 fallback 切换到 default 时仍会抖动。
inline-block 或 flex 布局保持骨架与真实内容流一致,避免 display 切换引发重排别把整个页面包进一个 Suspense —— 那会导致局部数据加载也触发全局骨架屏,体验反而更假。应该按功能区块拆分:用户信息区、评论列表区、推荐模块区各自独立使用 Suspense,每个都有专属骨架。
<Suspense> 对应一个逻辑上可独立加载的区域,比如 <UserCard /> 或 <CommentList />
defineAsyncComponent 导入,或组件 <script setup> 中含顶层 await(如 const data = await api.getUser()),Suspense 才能捕获<div> 或 <Fragment> 包裹,否则 Suspense 无法识别依赖树Suspense 自带 @pending、@resolve、@error 事件,可以补足骨架屏做不到的事:比如加载超时后显示重试按钮,或在 resolve 后加个淡入动画。
@pending 触发时可记录开始时间,配合 setTimeout 实现“加载 > 800ms 才显示骨架”,避免闪动@resolve 可触发 CSS 过渡类,例如给真实内容加 fade-in 类名,实现平滑入场@error 中可展示友好的错误提示,并提供“重试”按钮,点击后调用 key 强制刷新该 Suspense 区域在 Nuxt 或 Vue SSR 场景中,骨架屏可能被服务端直出,但客户端 hydrate 时 Suspense 又试图接管——容易报 “Hydration failed” 错误。
v-if="!$route?.meta?.ssrReady" 等运行时标记控制,而非服务端硬编码骨架loading 和 error 状态在 SSR 和 CSR 下行为一致,比如都用相同的 defineAsyncComponent({ loading: Skeleton })