⚙️ Vue3 传送门组件用过都说好 ⚙️

2,875 阅读3分钟

前言

Vue 一直鼓励我们通过将 UI 和相关行为封装到组件中来构建 UI。我们可以将它们嵌套在另一个内部,以构建一个组成应用程序 UI 的树。

然而,有时组件模板的一部分逻辑上属于该组件,而从技术角度来看,最好将模板的这一部分移动到 DOM 中 Vue app 之外的其他位置。

例如模态框、全屏loading等一些布满全屏的组件....

什么是teleport

teleport翻译成中文是传送的意思,该组件跟它的名字一样,它可以将我们的组件传送到指定的原生DOM元素中(Vue实例绑定的元素之外)。

使用场景

在实际项目开发中,我们常常会封装一些常用,复用性高的组件,例如全屏loading、模态框等组件,到这里有的同学就联想到了这些组件的显示层级(z-index)都要比平常页面组件要高。也就是说显示在最顶层的组件。

在传统的开发中由于样式的问题,我们常常把一个loading组件放在页面的最下面,先这样子的代码:

<template>
  <img alt="Vue logo" src="./assets/logo.png"/>
  <h3>teleport</h3>
  <!-- 其实loading组件应该放在这里更合适 --->
  <!-- 假设这里有很多代码 --->
  <loading/>
</template>
<div class="mask">
  <div class="loading">
   <!--忽略一些代码-->
  </div>
</div>
.mask {
  width: 100vw;
  height: 100vh;
  background: rgba(215, 215, 215, .5);
  position: absolute;
  inset: 0;
  z-index: 888;
  .loading {
    width: 100px;
    height: 100px;
    position: absolute;
    left: 50%;
    top: 50%;
    margin-top: -50px; /* 高度的一半 */
    margin-left: -50px; /* 宽度的一半 */
    z-index: 999;
  }  
}

1.gif

template中我们放入了一个loading组件,模拟当前正在处理一些事件。当这种模板结构上使用这个loading组件是正常的,我们看下这个渲染出来的DOM结构。

2.png

从图中,发现这个loading组件是嵌套在app组件中的,这个看只嵌套了一层是没什么问题,但这只是个简单的页面,但页面结构复杂时,这个loading将会出现深度嵌套问题,这里还有另外一个问题,这个loading组件是通过 以父级相对定位的 div 作为引用,当loading组件的父级组件设置为position: relative;时,这个loading组件就不能和上面图中的效果正常显示了。

我们尝试下,给loading组件套上一个带position: relative;属性的div

<template>
  <img alt="Vue logo" src="./assets/logo.png"/>
  <h3>teleport</h3>
    <div style="position: relative;">
      <loading/>
    </div>
</template>

3.png

现在loading组件是根据父级元素来定位的,由于父级元素设置了position: relative;属性,这导致它不能全屏占满来展示loading效果。那么怎么解决这个问题?

teleport组件的出现就是来解决这种情况的,改造下我们的loading组件:

<teleport to="body">
<div class="mask">
  <div class="loading">
    <!--忽略一些代码-->
  </div>
</div>
</teleport>

teleport组件中的to属性,这个属性告知teleport组件中DOM元素要渲染到哪个位置

loading组件中的内容套上teleport标签并填写上一个to属性,将包裹的HTML传送到body标签下。这样子我们的loading被传送到了body标签下了,不会受到原来父级div的影响而出现显示异常问题。

4.png

总结

结合teleport组件来用loading组件,可以将我们的loading传送的到我们指定的位置,也符合我们正常的思考逻辑,还能提高对loading组件的可复用性,即是一个页面中可以定制多个不同的loading组件来显示不同的效果。

本文参考:

v3.cn.vuejs.org/guide/telep…

v3.cn.vuejs.org/api/built-i…