美文网首页前端开发那些事儿
Vue 3新引入的Suspense组件介绍

Vue 3新引入的Suspense组件介绍

作者: microkof | 来源:发表于2021-01-04 16:51 被阅读0次

前言

Suspense是Vue 3新增的内置标签,尽管目前官方文档里并没有Suspense的介绍,但不妨碍我们先学习它。

每当我们希望组件等待数据获取时(通常在异步API调用中),我们都可以使用Vue3 Composition API制作异步组件。

以下是异步组件有用的一些实例:

  • 在页面加载之前显示加载动画
  • 显示占位符内容
  • 处理延迟加载的图像

以前,在Vue 2中,我们必须使用条件(例如 v-if 或 v-else)来检查我们的数据是否已加载并显示后备内容。

但是现在,Suspense随Vue3内置了,因此我们不必担心跟踪何时加载数据并呈现相应的内容。

父组件

我们通过范例学习Suspense,首先编写一个父组件。

  1. <template>

<template>里使用了<Suspense>标签,即便你完全不懂<Suspense>的用法,看了范例也该看明白,default插槽里要放正式内容,fallback插槽里要放降级内容,我放了一行字Loading ...,你也可以放菊花图之类的东西。

  1. <script setup>

变量名asyncCom必须与模板里的组件名一致。defineAsyncComponent方法用来动态引入组件。

<template>
  <div>
    子组件内容:
    <Suspense>
      <template #default>
        <async-com />
      </template>
      <template #fallback>Loading ...</template>
    </Suspense>
  </div>
</template>

<script setup>
import { defineAsyncComponent } from "vue";
const asyncCom = defineAsyncComponent(() => import("./asyncCom.vue"));
</script>

子组件(asyncCom.vue)

  1. <template>没什么可说的。

  2. <script setup>默认就是异步的,相当于async setup(){},所以你可以放心在里面写await。fetchData是我写的模拟ajax请求的Promise。

<template>
  <div>
    <ul>
      <li v-for="item in jsonData" :key="item.name">
        {{ item.name }} - {{ item.age }}
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref } from "vue";
function fetchData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        {
          name: "张三",
          age: 15,
        },
        {
          name: "李四",
          age: 17,
        },
      ]);
    }, 1500);
  });
}

ref: jsonData = await fetchData();
</script>

效果

页面会显示1.5秒的Loading...,然后显示一个列表。

原理

Vue从上到下执行子组件的setup里的全部语句,执行完同步语句(包括await语句)之后,父组件就认为子组件加载完成,在这之前,子组件setup状态始终未pending,所以父组件显示降级内容(Loading...),等子组件setup的状态变成resolved或者rejected,父组件就显示默认内容。

捕获异常

先说怎么显示异常:我的计划是,如果出现异常,就不再显示Loading...,而是显示“Loading Error. Retry?”,其中Retry做成一个按钮。

现在ref: jsonData = await fetchData();这句根本没有考虑异常情况,那么怎么捕获异常呢?

父组件

我们定义一个布尔值asyncComShow负责刷新组件,同时给<async-com>绑上事件@retry="retry"。retry函数要做的事情就是隐藏组件然后再显示,借此刷新组件。

<template>
  <div>
    <Suspense v-if="asyncComShow">
      <template #default>
        <async-com @retry="retry" />
      </template>
      <template #fallback> Loading ... </template>
    </Suspense>
  </div>
</template>

<script setup>
import { defineAsyncComponent, nextTick } from "vue";
const asyncCom = defineAsyncComponent(() => import("./asyncCom.vue"));
ref: asyncComShow = true;
function retry() {
  asyncComShow = false;
  nextTick(() => {
    asyncComShow = true;
  });
}
</script>

子组件

首先编写错误提示,然后定义变量errorShow来切换提示。

const instance = getCurrentInstance();用于提供emit方法向父组件发出retry申请。也可以在顶层定义const context = useContext();,然后用context.emit("retry");,我建议用后者,因为标准且轻量级。

之后,我们用try...catch...来捕获错误。

最后,await getList()的await是必须要写的,不然setup生命期会在瞬间结束,Loading提示也只会显示一瞬间。

<template>
  <div>
    <ul v-if="!errorShow">
      <li v-for="item in jsonData" :key="item.name">
        {{ item.name }} - {{ item.age }}
      </li>
    </ul>
    <div v-else>
      Loading Error. <button @click="retry">Retry?</button>
    </div>
  </div>
</template>

<script setup>
import { ref, nextTick, getCurrentInstance } from "vue";

const instance = getCurrentInstance();
ref: jsonData;
ref: errorShow = false;
function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject([
        {
          name: "张三",
          age: 15,
        },
        {
          name: "李四",
          age: 17,
        },
      ]);
    }, 1500);
  });
}

function retry() {
  instance.emit("retry");
}

async function getList() {
  errorShow = false;
  try {
    jsonData = await fetchData();
  } catch (e) {
    errorShow = true;
  }
}

await getList();
</script>

相关文章