# Suspense
新增

试验性

Suspense 是一个试验性的新特性,其 API 可能随时会发生变动。特此声明,以便社区能够为当前的实现提供反馈。

生产环境请勿使用。

# 介绍

在正确渲染组件之前进行一些异步请求是很常见的事。组件通常会在本地处理这种逻辑,绝大多数情况下这是非常完美的做法。

<suspense> 组件提供了另一个方案,允许将等待过程提升到组件树中处理,而不是在单个组件中。

一个常见的异步组件用例:


 
 
 

 










 




<template>
  <suspense>
    <template #default>
      <todo-list />
    </template>
    <template #fallback>
      <div>
        Loading...
      </div>
    </template>
  </suspense>
</template>

<script>
export default {
  components: {
    TodoList: defineAsyncComponent(() => import('./TodoList.vue'))
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

<suspense> 组件有两个插槽。它们都只接收一个直接子节点。default 插槽里的节点会尽可能展示出来。如果不能,则展示 fallback 插槽里的节点。

重要的是,异步组件不需要作为 <suspense> 的直接子节点。它可以出现在组件树任意深度的位置,且不需要出现在和 <suspense> 自身相同的模板中。只有所有的后代组件都准备就绪,该内容才会被认为解析完毕。

另一个触发 fallback 的方式是让后代组件从 setup 函数中返回一个 Promise。通常这是通过 async 实现的,而不是显式地返回一个 Promise:


 













export default {
  async setup() {
    // 在 `setup` 内部使用 `await` 需要非常小心
    // 因为大多数组合式 API 函数只会在
    // 第一个 `await` 之前工作
    const data = await loadData()

    // 它隐性地包裹在一个 Promise 内
    // 因为函数是 `async` 的
    return {
      // ...
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 子组件更新

一旦 <suspense>default 插槽里的内容被解析,则它只有在 default 根结点被替换的时候才能被再次触发。而树里的深层嵌套组件不足以让 <suspense> 回到等待状态。

如果根结点发生了变化,它会触发 pending 事件。然而,默认情况下,它不会更新 DOM 以展示 fallback 内容。取而代之的是,它会继续展示旧的 DOM,直到新组件准备就绪。这个行为可以通过 timeout prop 进行控制。这个值是一个毫秒数,告诉 <suspense> 组件多久之后展示 fallback。如果这个值是 0 则表示它在 <suspense> 进入等待状态时会立即显示。

# 事件

除了 pending 事件以外,<suspense> 组件还拥有 resolvefallback 事件。resolve 事件会在 default 插槽完成新内容的解析之后被触发。fallback 事件会在 fallback 插槽的内容展示的时候被触发。

这些事件可以用在诸如当新组件加载时在旧 DOM 上展示一个加载标识等场景。

# 和其它组件结合

<suspense><transition><keep-alive> 组件相结合是常见的情形。这些组件的嵌套顺序对于它们的正确工作很重要。

额外的,这些组件经常用于衔接 Vue Router(opens new window) <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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Vue Router 有内置的基于动态导入的组件懒加载(opens new window) 支持。它和异步组件有所区别,并且当前不会触发 <suspense>。不过它们仍然可以包含异步组件作为后代,这样它们还是可以正常触发 <suspense>