前言
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;
}
}
在template中我们放入了一个loading组件,模拟当前正在处理一些事件。当这种模板结构上使用这个loading组件是正常的,我们看下这个渲染出来的DOM结构。
从图中,发现这个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>
现在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的影响而出现显示异常问题。
总结
结合teleport组件来用loading组件,可以将我们的loading传送的到我们指定的位置,也符合我们正常的思考逻辑,还能提高对loading组件的可复用性,即是一个页面中可以定制多个不同的loading组件来显示不同的效果。
本文参考: