Vue 3.0新特性 | Teleport传送门与Suspense异步组件

498 阅读2分钟

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

⏳ 前言

本文是根据某课网Vue3系统精讲学习过程所做的笔记,如有偏差错误欢迎大佬指正ya!

🚪 Teleport--瞬移组件的位置

可以将子组件中使用的全局组件通过teleport渲染到全局dom节点下,避免造成组件嵌套过深,对组件造成干扰。例如,将全局提示框toast挂载到与#app同级的全局DOM节点下:

  1. 定义锚点
  2. 在Toast组件中使用teleport
  3. 调用Toast组件
<!-- index.html -->
<body>
  <noscript>
    <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
  </noscript>
  <div id="app"></div>
  <!-- 定义锚点 -->
  <div id="toast"></div>
</body>
// Toast.vue
<template>
<teleport to="#toast">    // 将toast传送到id为toast的dom节点下
  <div class="container">
    Toast
  </div>
</teleport>
</template>

<script>
export default {
  name: "",
  setup() {
   return {
   }
  }
}
</script>

<style scoped>
</style>

image.png

🚚 Suspense--异步加载的新福音

Suspense,它有两个template-slot,刚开始会渲染#fallback的内容,直到达到某个条件后会渲染#default的内容。特别注意,如果使用Suspense的话,需要在setup中返回一个promise来管理组件的状态(resolve/reject)。

单个异步组件

首先先创建一个子组件,并命名为AsyncShow

// AsyncShow.vue
<template>
  <div class="container">
    <h1>{{result}}</h1>
  </div>
</template>

<script>
import { defineComponent } from "vue"
export default defineComponent({
  name: "",
  setup() {
   return new Promise((resolve) => {
     setTimeout(() => {
       return resolve({
         result: 3000
       })
     }, 3000)
   })
  }
})
</script>

<style>
</style>

然后在App组件中调用AsyncShow

<template>
  <div id="app">
    <Suspense>
      <template #default>
        <AsyncShow />
      </template>
      <template #fallback>
        Loading...
      </template>
    </Suspense>
  </div>
</template>

<script>
import AsyncShow from './components/AsyncShow.vue';
export default {
    name: "App",
    setup() {
      // ...
    },
    components: { AsyncShow }
}

</script>

<style>
</style>

运行项目可以看到页面中一开始展示组件fallback时的状态Loading,三秒后当组件resolved后,切换至default状态的结果3000

多个异步组件

同样的,我们可以同时调用多个异步组件, 新建一个CatShow组件用于异步请求catapi获取猫片

<template>
  <div class="container">
    <img :src="result && result.url" alt="">
  </div>
</template>

<script>
import { defineComponent } from "vue"
import axios from 'axios'
export default defineComponent({
  name: "",
  async setup() {
    const rawData = await axios.get('https://api.thecatapi.com/v1/images/search')
    console.log(rawData);
    return {
      result: rawData.data[0]
    }
  }
})
</script>

然后在App组件中调用:

<template>
  <div id="app">
    <Suspense>
      <template #default>
        <div>
          <AsyncShow />
          <CatShow />
        </div>
      </template>
      <template #fallback>
        Loading...
      </template>
    </Suspense>
  </div>
</template>

<script>
import AsyncShow from './components/AsyncShow.vue';
import CatShow from './components/CatShow.vue';
export default {
    name: "App",
    setup() {
      // ...
    },
    components: { AsyncShow, CatShow }
}

</script>

运行后可以看到一开始页面显示Loading,3s后页面显示AsyncShow的结果3000和CatShow返回的一张猫片 image.png

抓取错误

在使用Suspense请求多个异步组件后,我们需要学习如何抓取异步请求的错误,毕竟网络请求可能会各种各样的原因引起错误,我们应该如何抓取Suspense包裹下的组件发生的错误呢?

我们可以使用Vue3自带的钩子函数onErrorCaptured,这个钩子函数需要返回一个布尔值,表示是否向上传播

<template>
  <div id="app">
    <Suspense>
      <template #default>
        <div>
          <AsyncShow />
        <CatShow />
        </div>
      </template>
      <template #fallback>
        Loading...
      </template>
    </Suspense>
    {{error}}
  </div>
</template>

<script>
import { computed, reactive, ref, toRefs } from '@vue/reactivity';
import { onErrorCaptured } from '@vue/runtime-core';
import AsyncShow from './components/AsyncShow.vue';
import CatShow from './components/CatShow.vue';
export default {
    name: "App",
    setup() {
      const error = ref(null)
      onErrorCaptured((e) => {
        error.value = e
        return true
      })
        return {
            error
        };
    },
    components: { AsyncShow, CatShow }
}

</script>

然后我们在CatShow中将请求url修改

const rawData = await axios.get('https://dog.ceo/api/breeds/image')

我们可以在页面中看到钩子函数捕获到的错误了

image.png