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>
相关推荐
碎碎念_4923 小时前
SpringBoot + Vue 前后端分离从 0 到 1 完整环境配置流程
vue.js·spring boot·后端
shuaijie05183 小时前
强制修改调用接口的api地址。
javascript·vue.js·ecmascript
katiua7 小时前
Vue-i18n TypeScript 补全提示
vue.js
Fuzio7 小时前
用 Spring Boot + Vue + Fuzio 构建现代 Java 桌面应用
vue.js·spring boot
kisshyshy7 小时前
从0到1彻底理解流式输出:先读懂Vue的“乐高积木”,再拧开LLM的“数据水龙头”
javascript·vue.js·人工智能
饮茶三千8 小时前
电子保函模板编辑器实战二:HTML→FTL 编译转换的设计与实现
前端·vue.js·设计模式
英勇无比的消炎药8 小时前
表单筛选利器:TinyVue BaseSelect 基础选择器实战
vue.js
先吃饱再说8 小时前
从零理解 Vue 3:组件化、数据绑定与响应式完全指南
vue.js·前端框架
gis开发之家10 小时前
《Vue3 从入门到大神29篇》单元测试与 E2E 测试 —— 保障 Vue3 项目的质量
前端·javascript·vue.js·单元测试·前端框架·vue3
forever_world10 小时前
# 💧 从前端视角,彻底搞懂 AI 流式输出 —— 从"接根水管"到 Vue 3 实战
前端·vue.js