Vue3应用——关于Teleport

804 阅读3分钟

小知识,大挑战!本文正在参与「程序员必备小知识」创作活动

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金

关于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 , perspectivefilter 属性不为 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 元素中。

  不过,即使在不同的地方渲染子组件 ,它仍将是父组件的子级,并将从中接收 nameprop ,这也意味着来自父组件的注入会正常工作,在 Vue Devtools 中你会看到子组件嵌套在父组件之下,而不是出现在他会被实际移动到的位置。

与诸君共勉,为自己加油!

参考文档

后记:Hello 小伙伴们,如果觉得本文还不错,记得点个赞或者给个 star,你们的赞和 star 是我编写更多更丰富文章的动力!GitHub 地址

文档协议


db 的文档库db 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于github.com/danygitgit上的作品创作。
本许可协议授权之外的使用权限可以从 creativecommons.org/licenses/by… 处获得。