1、teleport是什么

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

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

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

模态框

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

复制代码

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

模态框显示

大家可以看到,通过 teleport 的方式,现在的模态框成功显示在 idappdiv 同一层下,达到了相互独立,而不再是父子层级的结果。

在上面的案例中,我们学习到了通过使用 vue3 新推出的 teleport 特性,将组件渲染到另外一个 DOM 节点的方法,这样使得组件之间的独立性更强。

二、