最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • vue3的传送门teleport究竟有多神奇?suspense发起异步请求有多简约?

    正文概述 掘金(星期一研究室)   2021-06-26   740

    ⏳前言

    这是我参与更文挑战的第26天,活动详情查看:更文挑战

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

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

    一起来学习吧~?

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

    1、teleport是什么

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

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

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

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

    因此, 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>
    
    

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

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

    大家可以看到,通过 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>
    
    

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

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

    大家可以看到,通过 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>
    

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

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

    大家可以看到,我们同时发起了两个异步请求,并且在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')
    

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

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

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

    三、?️结束语

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

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


    起源地下载网 » vue3的传送门teleport究竟有多神奇?suspense发起异步请求有多简约?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元