Vue3你真的熟悉吗?04-哆啦A梦的任意门-teleport

原题链接

题目:

看到这个题大致的意思是 vue 有提供一个内置组件,用于将插槽内容渲染到另外的一个 Dom 元素中,变成改 Dom 元素的一部分。单看描述谁知道是什么鬼意思是吧?让我们一起来看看吧。

分析:

让我们来看看 vue 官方文档对 teleport 的介绍吧。一个内置组件,可以将组件内部的一部分模板,传送到该组件 Dom 结构的外层去。并且还举了一个模态框的例子。巴拉巴拉巴拉,其实我们可以简单去理解,它就像一个哆啦 A 梦的任意门,能够将东西瞬移到其他地方。

举例:

俗话说得好,好记性不如烂笔头是吧,那么前端有啥烂笔头,不就你那副玩烂的键盘么。

首先我们定义了一个父组件,它包含一个子组件Child

javascript 复制代码
// 父组件
<template>
  <div class="layout">
    <Child />
    <div class="box">首页的一个盒子</div>
  </div>
</template>

<script setup>
import Child from "./components/Child/index.vue";
</script>

<style lang="scss" scoped>
.layout {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;

  .box {
    z-index: 4;
    display: flex;
    align-items: center;
    width: 200px;
    height: 200px;
    background-color: pink;
  }
}
</style>

然后我们定义一个 Child 组件,里面又引用了 MyModal 模态框组件。

javascript 复制代码
// 子组件
<template>
  <div class="child">
    <div class="btn" @click="handleOpen" v-if="!isVisible">点击弹窗</div>
    <div class="content"><MyModal v-model:visible="isVisible" /></div>
  </div>
</template>

<script setup>
import { ref } from "vue";
import MyModal from "../my-modal/index";

const isVisible = ref(false);
const handleOpen = () => {
  isVisible.value = true;
};
</script>

<style lang="scss" scoped>
.child {
  .btn {
    cursor: pointer;
    padding: 5px 10px;
    margin-bottom: 10px;
    color: #1e80ff;
    border: 1px solid rgba(30, 128, 255, 0.3);
    border-radius: 5px;
    background-color: rgba(30, 128, 255, 0.05);
  }
}
</style>

最后是 MyModal 模态框组件的逻辑。

javascript 复制代码
// 模态框
<template>
  <div>
    <div class="modal" v-if="visible">
      <div class="header">
        <div class="btn" @click="handleClose">X</div>
      </div>
      <div class="content">我是弹窗内容</div>
    </div>
  </div>
</template>

<script setup name="MyModal">
import { defineProps, defineEmits } from "vue";

const emit = defineEmits(["update:visible"]);
defineProps({
  visible: {
    type: Boolean,
    default: false,
  },
});

const handleClose = () => {
  emit("update:visible", false);
};
</script>

<style lang="scss" scoped>
.modal {
  position: fixed;
  z-index: 999;
  top: 20%;
  left: 50%;
  width: 300px;
  margin-left: -150px;
  width: 200px;
  height: 200px;
  background-color: #ccc;
  .header {
    display: flex;
    justify-content: flex-end;
    .btn {
      cursor: pointer;
    }
  }
}
</style>

分析:

以上代码就是一个很简单的点击弹窗按钮弹出模态框,点击模态框关闭按钮关闭模态框的功能。让我们来看一下它此时的渲染情况。

虽然模态框正常渲染了,但是此时我们可以看到,这个模态框的层级是经历了很多层的,假设它外层的组件如果设置的 z-index 层级每个都不同,那我们是不是要不停去设置我们模态框的 z-index 属性来适配这种情况。如果写了一些特殊的样式,是不是也会不小心破坏模态框的布局。也就是说从用户感知的角度来说,my-modal 应该是一个独立的组件。从 Dom 结构应该完全剥离 Vue 顶层挂载的组件 Dom。简单来讲就是我既希望在组件内部用 my-modal,但是又不希望渲染在嵌套的组件 Dom 中。所以这就是teleport设置的初衷。

那么使用teleport,根据官方所描述应该会被传送到body标签下让我们来试试看。

javascript 复制代码
<template>
  <div>
    <teleport to="body">
      <div class="modal" v-if="visible">
        <div class="header">
          <div class="btn" @click="handleClose">X</div>
        </div>
        <div class="content">我是弹窗内容</div>
      </div>
    </teleport>
  </div>
</template>

<script setup name="MyModal">
import { defineProps, defineEmits } from "vue";

const emit = defineEmits(["update:visible"]);
defineProps({
  visible: {
    type: Boolean,
    default: false,
  },
});

const handleClose = () => {
  emit("update:visible", false);
};
</script>

<style lang="scss" scoped>
.modal {
  position: fixed;
  z-index: 999;
  top: 20%;
  left: 50%;
  width: 300px;
  margin-left: -150px;
  width: 200px;
  height: 200px;
  background-color: #ccc;
  .header {
    display: flex;
    justify-content: flex-end;
    .btn {
      cursor: pointer;
    }
  }
}
</style>

可以看到使用 teleport 后,真如任意门一样直接传送到 body 标签下了,不禁感慨,还有这种操作。

解决:

回到我们的挑战,所以这里我们要解决这个问题的话,直接用 teleportspan 渲染到 body 即可。

javascript 复制代码
<script setup>
const msg = "Hello World";
</script>

<template>
  <!-- 使用 teleport 将内容渲染到 body 的子元素中 -->
  <teleport to="body">
    <span>{{ msg }}</span>
  </teleport>
</template>
相关推荐
运维@小兵2 小时前
vue使用路由技术实现登录成功后跳转到首页
前端·javascript·vue.js
能来帮帮蒟蒻吗3 小时前
VUE3 -综合实践(Mock+Axios+ElementPlus)
前端·javascript·vue.js·笔记·学习·ajax·typescript
Java&Develop4 小时前
Vue ElementUI原生upload修改字体大小和区域宽度
vue.js
郭尘帅6665 小时前
vue3基础学习(上) [简单标签] (vscode)
前端·vue.js·学习
st紫月6 小时前
用vue和go实现登录加密
前端·vue.js·golang
岁岁岁平安7 小时前
Vue3学习(组合式API——计算属性computed详解)
前端·javascript·vue.js·学习·computed·计算属性
工业互联网专业9 小时前
基于springboot+vue的医院门诊管理系统
java·vue.js·spring boot·毕业设计·源码·课程设计·医院门诊管理系统
九月TTS9 小时前
TTS-Web-Vue系列:Vue3实现内嵌iframe文档显示功能
前端·javascript·vue.js
我爱加班、、10 小时前
Chrome安装最新vue-devtool插件
javascript·vue.js·chrome·vue-devtool
澄江静如练_10 小时前
小程序 存存上下滑动的页面
前端·javascript·vue.js