小知识,大挑战!本文正在参与「程序员必备小知识」创作活动
本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金
关于Teleport
create by db on 2021-10-20 19:00:36
Recently revised in 2021-10-20 19:43:41闲时要有吃紧的心思,忙时要有悠闲的趣味
一、Teleport介绍
关于 Teleport,可以先看一下官网介绍Vue. Teleport
Teleport 是 Vue3 新增的一个内置组件。正如名字 Teleport(传送) 一样这个组件允许我们将元素从一个地方移动到另一个地方,其作用主要用来将模板内的 DOM 元素移动到其他位置。
二、Teleport的使用场景及用法
业务开发的过程中,我们经常会封装一些常用的组件,例如 Modal 组件。相信大家在使用 Modal 组件的过程中,经常会遇到一个问题,那就是 Modal 的定位问题。
话不多说,我们先写一个正常的简单 Modal 组件。
<!-- NomalModal.vue-->
<template>
<!-- div.container 下弹窗组件正常展示。使用 fixed 进行布局的元素,在一般情况下会相对于屏幕视窗来进行定位,但是如果父元素的 transform, perspective 或 filter 属性不为 none 时,fixed 元素就会相对于父元素来进行定位。 -->
<div class="modal__mask">
<div class="modal__main">
<div class="modal__header">
<h3 class="modal__title">正常弹窗</h3>
<span class="modal__close">x</span>
</div>
<div class="modal__content">
正常弹窗文本内容
</div>
<div class="modal__footer">
<button>取消</button>
<button>确认</button>
</div>
</div>
</div>
</template>
<script>
export default {
setup() {
return {};
},
};
</script>
<style lang="less">
.modal__mask {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.3);
}
.modal__main {
margin: 0 auto;
margin-bottom: 5%;
margin-top: 20%;
width: 500px;
background: #fff;
border-radius: 8px;
}
.modal__header,
.modal__footer,
.modal__content {
border-bottom: 1px solid #ccc;
display: flex;
justify-content: center;
align-items: center;
}
.modal__content {
height: 100px;
}
.modal__header,
.modal__footer {
height: 50px;
}
.modal__title {
margin: 0;
flex: 1;
text-align: center;
}
.modal__close {
flex: 0 0 20px;
}
</style>
然后我们在父组件 TeleportDemo 中引入子组件 NomalModal 。
<!-- TeleportDemo.vue -->
<template>
<div class="container">
<NomalModal />
</div>
</template>
<script>
import NomalModal from '@/components/NomalModal.vue';
export default {
components: {
NomalModal
},
setup() {
return {};
}
};
</script>
<style lang="less">
.container {
height: 80vh;
overflow: hidden;
}
</style>
渲染后的结果如下:

从上图我们可以看到,当前弹窗组件正常展示。使用 position: fixed; 进行布局的元素,在一般情况下会相对于屏幕视窗来进行定位,正常情况下没有问题。但是如果父元素的 transform , perspective 或 filter 属性不为 none 时,fixed 元素就会相对于父元素来进行定位。
因此我们只需要把 .container 类的 transform 稍作修改,弹窗组件的定位就会错乱。
<style lang="less">.container {
height: 80vh;
overflow: hidden;
transform: translateZ(0);
}
</style>
渲染后的结果如下:

很明显,弹窗组件的定位给出现了问题,那我们应该怎么解决呢?现在Teleport 组件就派上用场了。
Teleport 提供了一种干净的方法,允许我们控制在 DOM 中哪个父节点下呈现 HTML,而不必求助于全局状态或将其拆分为两个组件。 -- Vue 官方文档
我们只需要将弹窗内容放入 <Teleport></Teleport> 内,并设置 to 属性为 body ,表示弹窗组件每次渲染都会做为 body 的子级,这样之前的问题就能得到解决。很简单的用法吧。
<template>
<teleport to="body">
<div class="modal__mask">
<div class="modal__main">
...
</div>
</div>
</teleport>
</template>
三、Teleport的原理
其实Teleport的原理很简单,就是将 Teleport 的子组件挂载到属性 to 对应的 DOM 元素中。
不过,即使在不同的地方渲染子组件 ,它仍将是父组件的子级,并将从中接收 name 、 prop ,这也意味着来自父组件的注入会正常工作,在 Vue Devtools 中你会看到子组件嵌套在父组件之下,而不是出现在他会被实际移动到的位置。
与诸君共勉,为自己加油!
参考文档
后记:Hello 小伙伴们,如果觉得本文还不错,记得点个赞或者给个 star,你们的赞和 star 是我编写更多更丰富文章的动力!GitHub 地址
文档协议
db 的文档库 由 db 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于github.com/danygitgit上的作品创作。
本许可协议授权之外的使用权限可以从 creativecommons.org/licenses/by… 处获得。