Vue3 Suspense 异步请求展示

24 min read

Suspense组件有两个template,fallback表示渲染请求期间的内容,default表示渲染请求完成后的内容:

<Suspense>
      <template #default>
        <请求完成后内容 />
      </template>
      <template #fallback>
        <h1>Loading !...</h1>
      </template>
    </Suspense>

异步组件

配合 vue3 中 defineAsyncComponent 来加载异步组件

defineAsyncComponent 接受一个函数作为参数,返回为import一个组件对象

通过v-if切换组件的加载过程

<template>
  <button @click="loadAsyncComponent">点击加载异步组件</button>
  <Suspense v-if="loadAsync">
    <template #default>
      <AsyncComponent></AsyncComponent>
    </template>
    <template #fallback>
      <div class="loading"></div>
    </template>
  </Suspense>
</template>

<script>
import { defineAsyncComponent } from "vue";
export default {
  name: "App",
  data() {
    return {
      loadAsync: false
    };
  },
  components: {
    HelloWorld,
    AsyncComponent: defineAsyncComponent(() => {
      return import("./components/async-component.vue");
    })
  },
  methods: {
    loadAsyncComponent() {
      this.loadAsync = true;
    }
  }
};
</script>

<style lang="scss">
#app {
  width: 800px;
  height: 600px;
  padding: 20px;
  margin: 0 auto;
  border: 1px solid #eeeeee;
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  position: relative;
}
button {
  padding: 12px 12px;
  background-color: #1890ff;
  outline: none;
  border: none;
  border-radius: 4px;
  color: #fff;
  cursor: pointer;
}
.loading {
  position: absolute;
  width: 36px;
  height: 36px;
  top: 50%;
  left: 50%;
  margin: -18px 0 0 -18px;
  background-image: url("./assets/images/Loading.png");
  background-size: 100%;
  animation: rotate 1.4s linear infinite;
}
@keyframes rotate {
  from {
    transform: rotate(0);
  }
  to {
    transform: rotate(360deg);
  }
}
</style>

实现数据加载中的效果

在setup()里面需要返回一个promise,使用Suspense组件引该组件时,请求完成之前会一起显示loading效果

<template>
  <img :src="result && result.message">
</template>
<script lang="ts">
import axios from 'axios'
import { defineComponent } from 'vue'
export default defineComponent({
  async setup() {
    const rawData = await axios.get('https://dog.ceo/api/breeds/image')
    return {
      result: rawData.data
    }
  }
})
</script>

实现路由加载效果

在路由完全加载前默认使用fallback里面的内容

<router-link to="/home">Home</router-link>|
<router-link to="/about">About</router-link>
<Suspense>
  <template #default>
    <router-view />
  </template>
  <template #fallback>
    <div class="loading"></div>
  </template>
</Suspense>