Vue3中Suspense组件未渲染fallback加载状态问题
1. Suspense 层级位置错误
你需要把 Suspense 放在父组件中,直接包裹 _GetShipment 子组件,而非在子组件内部用 Suspense 包裹自身内容。因为子组件使用了 async setup,它本身属于异步组件,只有父级的 Suspense 才能捕获到它的异步加载过程,进而触发 fallback 显示。
父组件正确写法示例:
<template> <div v-if="shipments.length === 1"> <Suspense> <template #default> <_GetShipment :id="shipments[0]" /> </template> <template #fallback> Loading... </template> </Suspense> </div> </template>
若将 Suspense 放在子组件内部,子组件的 async setup 已经在执行异步请求,内部的 Suspense 无法捕获该阶段的异步操作,自然不会显示加载提示。
2. Async setup 未正确抛出 Promise
Suspense 依赖组件 setup 抛出的 Promise 来触发等待状态。如果子组件的 async setup 中,fetch 请求被错误处理(比如用 try/catch 吞掉异常),或者没有让 Promise 正常传递,Suspense 就无法感知到异步操作的存在。
子组件正确的 setup 写法:
<script setup async> const props = defineProps(['id']) // async 函数本身会返回 Promise,直接 await 请求即可 const shipmentDetail = await fetch(`/api/shipment/${props.id}`).then(res => res.json()) </script>
如果需要使用 try/catch,记得捕获错误后重新抛出,保留异步状态让 Suspense 捕获:
<script setup async> const props = defineProps(['id']) let shipmentDetail try { shipmentDetail = await fetch(`/api/shipment/${props.id}`).then(res => res.json()) } catch (e) { // 处理错误后抛出,维持异步状态 throw e } </script>
3. 父组件响应式更新异常
检查父组件中的 shipments 是否是用 ref 或 reactive 声明的响应式变量。如果 Office.js 的 getAsync 回调中更新数组时未触发 Vue 的响应式更新,可能导致子组件渲染时机异常,Suspense 还没来得及显示 fallback,请求就已完成。
父组件响应式声明示例:
<script setup> import { ref } from 'vue' const shipments = ref([]) // Office.js 获取邮件正文逻辑 Office.context.mailbox.item.body.getAsync('text', (result) => { if (result.status === Office.AsyncResultStatus.Succeeded) { const body = result.value const shipmentId = // 你的正则匹配逻辑 shipments.value = [shipmentId] // 必须用 .value 更新 ref 数组 } }) </script>
4. Vue 版本兼容性问题
Suspense 对 async setup 的稳定支持是从 Vue 3.2 开始的,如果你的 Vue 版本过旧,可能存在兼容性问题,导致 fallback 不生效。建议升级到 Vue 3.2 及以上版本。
内容的提问来源于stack exchange,提问作者oliverbj

