五、suspense
在正确渲染组件之前进行一些异步请求是很常见的事。组件通常会在本地处理这种逻辑,绝大多数情况下这是非常完美的做法。
该 <suspense>
组件提供了另一个方案,允许将等待过程提升到组件树中处理,而不是在单个组件中。
<template>
<suspense>
<!-- <suspense> 组件有两个插槽。它们都只接收一个直接子节点 -->
<!-- default 插槽里的节点会尽可能展示出来。如果不能,则展示 fallback 插槽里的节点 -->
<template #default>
<todo-list />
</template>
<template #fallback>
<div>
Loading...
</div>
</template>
</suspense>
</template>
<script>
export default {
components: {
TodoList: defineAsyncComponent(() => import('./TodoList.vue'))
}
}
</script>
重要的是,异步组件不需要作为 <suspense>
的直接子节点。它可以出现在组件树任意深度的位置,且不需要出现在和 <suspense>
自身相同的模板中。只有所有的后代组件都准备就绪,该内容才会被认为解析完毕。
另一个触发 fallback
的方式是让后代组件从 setup
函数中返回一个 Promise。通常这是通过 async
实现的,而不是显式地返回一个 Promise:
export default {
async setup() {
// 在 `setup` 内部使用 `await` 需要非常小心
// 因为大多数组合式 API 函数只会在
// 第一个 `await` 之前工作
const data = await loadData()
// 它隐性地包裹在一个 Promise 内
// 因为函数是 `async` 的
return {
// ...
}
}
}
子组件更新
一旦 <suspense>
的 default
插槽里的内容被解析,则它只有在 default
根结点被替换的时候才能被再次触发。而树里的深层嵌套组件不足以让 <suspense>
回到等待状态。
如果根结点发生了变化,它会触发 pending
事件。然而,默认情况下,它不会更新 DOM 以展示 fallback
内容。取而代之的是,它会继续展示旧的 DOM,直到新组件准备就绪。这个行为可以通过 timeout
prop 进行控制( defineAsyncComponent 组件的 timeout )。这个值是一个毫秒数,告诉 <suspense>
组件多久之后展示 fallback
。如果这个值是 0
则表示它在 <suspense>
进入等待状态时会立即显示。
事件
除了 pending
事件以外, <suspense>
组件还拥有 resolve
和 fallback
事件。 resolve
事件会在 default
插槽完成新内容的解析之后被触发。 fallback
事件会在 fallback
插槽的内容展示的时候被触发。
这些事件可以用在诸如当新组件加载时在旧 DOM 上展示一个加载标识等场景
和其它组件结合
将 <suspense>
跟 ](https://v3.cn.vuejs.org/api/built-in-components.html#transition) 和 [
组件相结合是常见的情形。这些组件的嵌套顺序对于它们的正确工作很重要。
额外的,这些组件经常用于衔接 Vue Router 的 <router-view>
组件。
以下示例展示了如何嵌套这些组件以让它们的表现符合预期。若要简化这个组合你可以移除不需要的组件:
<router-view v-slot="{ Component }">
<template v-if="Component">
<transition mode="out-in">
<keep-alive>
<suspense>
<component :is="Component"></component>
<template #fallback>
<div>
Loading...
</div>
</template>
</suspense>
</keep-alive>
</transition>
</template>
</router-view>
Vue Router 有内置的基于动态导入的 组件懒加载 支持。它和异步组件有所区别,并且当前不会触发 <suspense>
。不过它们仍然可以包含异步组件作为后代,这样它们还是可以正常触发 <suspense>
。
用 onErrorCaptured
捕获 suspense
中出现的错误
<script>
import { ref,onErrorCaptured } from 'vue';
setup() {
const captureError = ref(null);
onErrorCaptured((e: any) => { // 捕获 suspense 中可能的错误
captureError.value = e;
return true; // 返回一个布尔值,表示是否继续向上传播
})
return {
captureError
}
}
</script>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论