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>
相关推荐
杨筱毅3 分钟前
【技术选型】前端框架:Vue vs React - 组合式API与Hooks的哲学之争
vue.js·react.js·前端框架·技术选型
Sheldon一蓑烟雨任平生10 小时前
Vue3 依赖注入(provide 和 inject)
vue.js·inject·provide·vue3 依赖注入·跨级别组件通信
paopaokaka_luck11 小时前
基于SpringBoot+Vue的助农扶贫平台(AI问答、WebSocket实时聊天、快递物流API、协同过滤算法、Echarts图形化分析、分享链接到微博)
java·vue.js·spring boot·后端·websocket·spring
Sheldon一蓑烟雨任平生12 小时前
Vue3 重构待办事项(主要练习组件化)
vue.js·重构·vue3·组件化练习
zuo-yiran16 小时前
vue div标签可输入状态下实现数据双向绑定
前端·javascript·vue.js
qq_3168377516 小时前
使用leader-line-vue 时垂直元素间距过小连线打转的解决
前端·javascript·vue.js
abiao198117 小时前
VUE的“单向数据绑定” 和 “双向数据绑定”
前端·javascript·vue.js
麦麦大数据17 小时前
F042 A星算法课程推荐(A*算法) | 课程知识图谱|课程推荐vue+flask+neo4j B/S架构前后端分离|课程知识图谱构造
vue.js·算法·知识图谱·neo4j·a星算法·路径推荐·课程推荐
拉不动的猪17 小时前
多窗口数据实时同步常规方案举例
前端·javascript·vue.js
南清的coding日记18 小时前
Java 程序员的 Vue 指南 - Vue 万字速览(01)
java·开发语言·前端·javascript·vue.js·css3·html5