vue3的传送门teleport究竟有多神奇?suspense发起异步请求有多简约?

简介: 该文章介绍了Vue3中新特性Teleport和Suspense的使用方法,演示了如何使用Teleport进行DOM节点的非父子关系传送,以及Suspense在处理异步组件加载时的优雅展示和错误处理技巧。

teleport和suspense

大家都知道,在我们平常的前端开发中,实现模态框和发起异步请求是再常见不过的事情了。但是呢,不管是用vue2和原生js的实现方式,从逻辑上来说都还不够独立,因此,vue3推出了新的方法来解决此问题。

下面就带领大家一起来了解vue3新推出的 teleport 究竟有多神奇?以及如何用 suspense 发起多个异步的请求?

一起来学习吧~📚

一、👋用teleport实现打开模态框操作

1、teleport是什么

teleport,字面意思就是远距离传送,我们可以把它理解为传送门的意思。

大家都知道,传送门的意思就是从一个地方传送到了另外一个地方。而 vue3 为什么要用 teleport 来表达呢?

其实,有一个非常常见的需求就是,我们经常要通过点击一个按钮,来实现模态框的效果。而在 vue3 之前,我们基本上控制它都是点击后上下会形成一个父子组件的关系,这样子感觉独立性就没有那么强了。

模态框

因此, vue3 为了解决该问题,就用了 teleport 来解决。 teleport 就仿佛一个传送门,像上图这样,比如我们点击了打开按钮,那么点击完了之后,使用传送门瞬间移动到另外一个地方(模态框 Model )。再点击关闭按钮传送门模态框 Modal 就消失了。

通过这样的解释,相信大家对 teleport 有了一个基础的认识。

2、实现模态框功能

接下来我们就来用这个功能,实现一个模态框,控制组件的显示和隐藏。

(1)设置锚点

我们现在 vue3 项目下的 /public/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="modal"></div>
    <!-- built files will be auto injected -->
  </body>

(2)定义子组件

接下来我们在 /src/components 下定义一个子组件,命名为 Modal.vue具体代码如下:

<template>
  <teleport to="#modal">
      <div id="center" v-if="isOpen">
          <h2><slot>this is a modal</slot></h2>
          <button class="btn2" @click="buttonClick">Close</button>
      </div>
    </teleport>
</template>

<script>
import {
     defineComponent } from 'vue'
export default defineComponent({
    
    //父组件的数据需要通过props把数据传给子组件,props的取值可以是数组也可以是对象
    props: {
    
        isOpen: Boolean,
    },
    //子组件向父组件传递数据
    //使用emits,更明确的显示组件的自定义事件有哪些
    emits: {
    
        'close-modal': null
    },
    //props对应props的内容
    //context名字可以自定义,只要上下对应即可,用来触发emit的内容
    setup(props, context){
    
        const buttonClick = () => {
    
            context.emit('close-modal')
        }
        return{
    
            buttonClick
        }
    }
})
</script>

<style>
#center{
    
    width:200px;
    height:200px;
    border:2px solid rgb(105, 165, 56);
    text-align: center;
    border-radius: 2px;
    margin: 50px auto 0;
}
.btn2{
    
  background: #1971c9;
  border:none;
  padding: 8px;
  border-radius: 5px;
  color: #fff;
  cursor: pointer;
}
</style>

(3)定义父组件

之后我们再来定义一个父组件,命名为 index.vue具体代码如下:

<template>
    <button class="btn1" @click="openModal">打开模态框</button>
    <modal :isOpen="modalIsOpen" @close-modal="onModalClose">
      My Modal!!!
    </modal>
</template>

<script lang="ts">
import {
     ref, defineComponent} from 'vue'
import Modal from './components/Modal.vue'

export default defineComponent({
    
  name: 'App',
  components: {
    
    Modal
  },
  setup(){
    
    //添加响应式对象控制是否显示
    const modalIsOpen = ref(false)
    //打开模态框事件
    const openModal = () => {
    
      modalIsOpen.value = true
    }
    //关闭模态框事件
    const onModalClose = () => {
    
      modalIsOpen.value = false
    }

    return{
    
      modalIsOpen,
      openModal,
      onModalClose
    }
  }
});
</script>

<style>
#app {
    
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2d4c6b;
  margin-top: 60px;
}
.btn1{
    
  background: #1971c9;
  border:none;
  padding: 16px;
  border-radius: 5px;
  color: #fff;
  cursor: pointer;
}
</style>

现在我们来看下浏览器的显示效果:

模态框显示

大家可以看到,通过 teleport 的方式,现在的模态框成功显示在 idappdiv 同一层下,达到了相互独立,而不再是父子层级的结果。

在上面的案例中,我们学习到了通过使用 vue3 新推出的 teleport 特性,将组件渲染到另外一个 DOM 节点的方法,这样使得组件之间的独立性更强。

二、🤚用Suspense

1、Suspense是什么

我们都知道,在 web 世界中,经常遇到很多的异步请求困境。在发起异步请求时,我们往往需要去判断这些异步请求的状态,然后呢,根据这些请求来展示不同的界面。

那现在呢, vue3 推出了一个新的内置组件 SuspenseSuspense 是一个特殊的组件,它会有两个 template slot ,刚开始会渲染 feedback 内容,直到达到某个条件以后,才会渲染正式的内容,也就是default的内容。这样呢,进行异步内容的渲染就会变得特别简单。

同时值得注意的是,如果使用 Suspense ,要返回一个 promise 而不是一个对象。

2、用Suspense发起一个异步请求

接下来我们使用 Suspense发起一个异步请求

首先我们在项目下定义一个子组件,命名为 AsyncShow.vue具体代码如下:

<template>
  <h1>{
  {result}}</h1>
</template>
<script lang="ts">
import {
     defineComponent } from 'vue'
export default defineComponent({
    
  setup() {
    
    //使用Suspense需要返回一个对象
    return new Promise((resolve) => {
    
      setTimeout(() => {
    
        return resolve({
    
          result: '10000'
        })
      }, 3000)
    })
  }
})
</script>

之后在项目下再定义一个父组件,命名为 DogShow.vue具体代码如下:

<template>
  <div id="app">
    <Suspense>
      <template #default>
        <async-show />
      </template>
      <template #fallback>
        <h1>Loading !...</h1>
      </template>
    </Suspense>
  </div>
</template>

<script lang="ts">
import {
     defineComponent } from 'vue'
import AsyncShow from './components/AsyncShow.vue'

export default defineComponent({
    
  name: 'App',
  components: {
    
    AsyncShow,
  },
  setup() {
    

  }
});
</script>

<style>
#app {
    
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
h1 {
    
  font-size: 6rem;
}
</style>

最终浏览器的显示效果如下:

发起一个异步请求

大家可以看到,通过 Suspense ,可以很轻易的发起一个异步请求。刚开始是fallback状态,之后达到 3s 的时间之后,切换到default的状态,显示出对应的异步请求内容。

3、用Suspense发起多个异步请求

我们不满足于现状,且互联网千奇百怪的,我们总不能一直只发送一个异步请求吧!所以,接下来我们就来实现发起多个异步请求的效果。

首先我们用一个免费的在线API ,来发起一个请求。接下来我们在项目的 components 文件下,再定义一个子组件,命名为 DogShow.vue具体代码如下:

<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/random')
    return {
    
      result: rawData.data
    }
  }
})
</script>

接下来我们再把以上子组件的内容添加到父组件中,具体代码如下:

<template>
  <div id="app">
    <Suspense>
      <template #default>
        <async-show />
        <dog-show />
      <template #fallback>
        <h1>Loading !...</h1>
      </template>
    </Suspense>
  </div>
</template>

<script lang="ts">
import AsyncShow from './components/AsyncShow.vue'
import DogShow from './components/DogShow.vue'

export default {
    
  name: 'App',
  components: {
    
    AsyncShow,
    DogShow
  },
  setup() {
    

  }
};
</script>

<style>
#app {
    
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
h1 {
    
  font-size: 6rem;
}
</style>

最终浏览器的显示效果如下:

发起多个请求

大家可以看到,我们同时发起了两个异步请求,并且在Suspense中的default插槽里面同时使用。同样的,浏览器会先显示fallback的内容,之后等到时机到了,就显示我们请求的内容。

依据这样的例子,显示更多的请求也同样有效。这样对比起来,发送多个异步请求是不是就方便了许多。

4、如何抓取错误

学完上面的内容,相信大家对Suspense的用法已经有所了解。那么,网络请求千奇百怪的,总不能每次都能够很顺畅的发起请求对吧。所以呢,我们来需要再来学习一下,当网络请求失败时,如何抓取Supsense包裹下的错误

这个时候我们可以使用一个钩子函数,这个函数叫做 onErrorCaptured ,接下来我们来看下怎么抓取。

我们将父组件 index.vue 进行改造,具体代码如下:

<template>
  <div id="app">
    <p>{
  {error}}</p>
    <Suspense>
      <template #default>
        <async-show />
        <dog-show />
      <template #fallback>
        <h1>Loading !...</h1>
      </template>
    </Suspense>
  </div>
</template>

<script lang="ts">
import {
     onErrorCaptured } from 'vue'
import AsyncShow from './components/AsyncShow.vue'
import DogShow from './components/DogShow.vue'

export default {
    
  name: 'App',
  components: {
    
    AsyncShow,
    DogShow
  },
  setup() {
    
    const error = ref(null)
    onErrorCaptured((e: any) => {
    
      error.value = e
      return true
    })
    return{
    
        error
    }
  }
};
</script>

<style>
#app {
    
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
h1 {
    
  font-size: 6rem;
}
</style>

此时我们再将 DogShow.vue 的接口进行修改,让其变成一个无效的API。代码如下:

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

接下来我们就来看一下浏览器的运行效果:

如何抓取错误

大家可以看到,修改成无效的 API 后,狗狗的图片也不显示了,最后最上方就是通过 onErrorCaptured 这个生命周期捕捉到的错误,清晰明了。

三、🖐️结束语

到这里, teleportSuspense 的内容就讲解结束啦!相信大家对传送门的神奇之处也有了一定的了解,对 Suspense 的独到之处也感受了一番。

vue3持续学习,更新永不停歇……

  • 关注公众号 星期一研究室 ,第一时间关注学习干货,更多有趣的专栏待你解锁~
  • 如果这篇文章对你有用,记得 一键三连 再走哦~
  • 我们下期见!🥂🥂🥂
相关文章
|
10天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
6天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2506 14
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
6天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1519 14
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
|
8天前
|
编解码 JSON 自然语言处理
通义千问重磅开源Qwen2.5,性能超越Llama
击败Meta,阿里Qwen2.5再登全球开源大模型王座
530 13
|
1月前
|
运维 Cloud Native Devops
一线实战:运维人少,我们从 0 到 1 实践 DevOps 和云原生
上海经证科技有限公司为有效推进软件项目管理和开发工作,选择了阿里云云效作为 DevOps 解决方案。通过云效,实现了从 0 开始,到现在近百个微服务、数百条流水线与应用交付的全面覆盖,有效支撑了敏捷开发流程。
19282 30
|
1月前
|
人工智能 自然语言处理 搜索推荐
阿里云Elasticsearch AI搜索实践
本文介绍了阿里云 Elasticsearch 在AI 搜索方面的技术实践与探索。
18836 20
|
1月前
|
Rust Apache 对象存储
Apache Paimon V0.9最新进展
Apache Paimon V0.9 版本即将发布,此版本带来了多项新特性并解决了关键挑战。Paimon自2022年从Flink社区诞生以来迅速成长,已成为Apache顶级项目,并广泛应用于阿里集团内外的多家企业。
17524 13
Apache Paimon V0.9最新进展
|
8天前
|
人工智能 自动驾驶 机器人
吴泳铭:AI最大的想象力不在手机屏幕,而是改变物理世界
过去22个月,AI发展速度超过任何历史时期,但我们依然还处于AGI变革的早期。生成式AI最大的想象力,绝不是在手机屏幕上做一两个新的超级app,而是接管数字世界,改变物理世界。
458 48
吴泳铭:AI最大的想象力不在手机屏幕,而是改变物理世界
|
1天前
|
云安全 存储 运维
叮咚!您有一份六大必做安全操作清单,请查收
云安全态势管理(CSPM)开启免费试用
354 4
叮咚!您有一份六大必做安全操作清单,请查收
|
2天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。