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 小时前
RouYi-Vue框架,环境搭建以及使用
前端·javascript·vue.js
ᥬ 小月亮3 小时前
Vue中接入萤石等直播视频(更新中ing)
前端·javascript·vue.js
呜呼~225144 小时前
前后端数据交互
java·vue.js·spring boot·前端框架·intellij-idea·交互·css3
Ares码农人生6 小时前
React 高级组件开发:动态逻辑与性能优化
vue.js·前端框架
嘤嘤怪呆呆狗7 小时前
【开发问题记录】执行 git cz 报require() of ES Module…… 错误
前端·javascript·vue.js·git·vue
ganlanA10 小时前
uniapp+vue 前端防多次点击表单,防误触多次请求方法。
前端·vue.js·uni-app
ZHYCH10 小时前
图片加载失败重试,重试至预期次数使用占位图
前端·vue.js
程序员_三木10 小时前
在 Vue3 项目中安装和配置 Three.js
前端·javascript·vue.js·webgl·three.js
lxw184491251410 小时前
vue 基础学习
前端·vue.js·学习
徐_三岁10 小时前
Vue3 Suspense:处理异步渲染过程
前端·javascript·vue.js