一文了解——内置组件Teleport附带讲解源码(Vue.js)

前言

很多时候,要写一个弹窗类似的组件,总是会将部分心神消耗在position的定位上。得考虑到父组件是怎么样的情况,会发生什么样子的事情,会不会对外面的组件样式造成影响。那有没有一点可以节约思绪的方法呢?那肯定是有的在Vue的官网上有那么一个内置组件Teleport就可以解决到这个问题。

Teleport的概念

直观一点摘要Vue官网的一段话 : <Teleport> 是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 。这就是他的最为简单的讲解。

创建文件以及代码内容

首先我们创建一下文件,格式如下:

image.png

代码可以直接拷贝

index.vue文件内容

<template>
    <div class="body">
        <h1>父级</h1>
        <A></A>
    </div>
</template>

<script setup lang="ts">
import A from './a.vue'
</script>

<style lang="scss">
.body {
    background-color: rgb(81, 255, 0);
    height: 50vh;
}
</style>

a.vue文件内容

<template>
 
    <div class="dialog">
        <header class="header">
            <div>弹框</div>
            <el-icon>
                <CloseBold />
            </el-icon>
        </header>
        <main class="main">
            输出内容
        </main>
        <footer class="footer">
            <el-button >取消</el-button>
            <el-button >确定</el-button>
        </footer>
    </div>
 
</template>
 
<script setup lang='ts'>
import { ref, reactive } from 'vue'
 
</script>
<style lang="less" scoped>
.dialog {
    width: 400px;
    height: 400px;
    background: #141414;
    display: flex;
    flex-direction: column;
    position: absolute;
    left: 50%;
    top: 50%;
    margin-left: -200px;
    margin-top: -200px;
 
    .header {
        display: flex;
        color: #CFD3DC;
        border-bottom: 1px solid #636466;
        padding: 10px;
        justify-content: space-between;
    }
 
    .main {
        flex: 1;
        color: #CFD3DC;
        padding: 10px;
    }
 
    .footer {
        border-top: 1px solid #636466;
        padding: 10px;
        display: flex;
        justify-content: flex-end;
    }
}
</style>

展示出来的效果如下

image.png

可以看到在a.vue的文件中,我们是通过绝对定位计算位置的方式来实现居中对齐的,但是如果在index.vue文件中我们加入了相对定位 position: relative; 就会有这样子的效果:

image.png

没错,它来到了上面,这是因为绝对定位的父级没有加入相对定位的话,会相对于页面窗口去定位,而父组件使用了相对定位等类似的办法,就会改变他的锚点,会相对于父级去定位。

而Teleport就是对于这个问题做了一层处理。

那就让我们在index.vue尝试加入 Teleport吧!

Teleport的使用以及QA

我们只是需要直观的使用 Teleport 将原本的 A组件包裹起来。

就可以做到我们想要的效果

代码可以直接拷贝

index.vue文件内容

<template>
    <div class="body">
        <h1>父级</h1>
        <Teleport to="body">
            <A></A>
        </Teleport>
    </div>
</template>

<script setup lang="ts">
import A from './a.vue'
</script>

<style lang="scss">
.body {
    background-color: rgb(81, 255, 0);
    height: 50vh;
    position: relative;
}
</style>

Q:你可能会问 这个to是啥?为什么Teleport不需要引入?

A:首先来解答一下为什么Teleport不需要引入?因为标题的时候已经写到了他本质上是一个内置组件,在后面中会讲到从他的源码中可以获悉,其实Teleport并没有做出来什么渲染的操作,只是单纯的挂载到目标位置去。

Q:那么目标位置又是什么呢?

A:to就是目标元素。<Teleport> 接收一个 to prop 来指定传送的目标。to 的值可以是一个 CSS 选择器字符串,也可以是一个 DOM 元素对象。这段代码的作用就是告诉 Vue“把以下模板片段传送到 body 标签下”。

Q:那么又有一个问题了?假如我不需要这个了呢?我想要一个动态的可以做到我需要的时候在外面,不需要的时候挂载在父组件下面?

A:<Teleport>也提供了选择,在某些场景下可能需要视情况禁用 <Teleport>。举例来说,我们想要在桌面端将一个组件当做浮层来渲染,但在移动端则当作行内组件。我们可以通过对 <Teleport> 动态地传入一个 disabled prop 来处理这两种不同情况。

如果看完这一段你觉得好像是理解了的话,其实这里还埋下了一个大坑。还记得第二个QUESTION中的这一句话嘛?to 的值可以是一个 CSS 选择器字符串,也可以是一个 DOM 元素对象。

<Teleport> 挂载时,传送的 to 目标必须已经存在于 DOM 中。理想情况下,这应该是整个 Vue 应用 DOM 树外部的一个元素。如果目标元素也是由 Vue 渲染的,你需要确保在挂载 <Teleport> 之前先挂载该元素。

Teleport的源码

首先你需要去github上面拉取一份源码。
找到如下目录:

image.png

下拉到358行上下

teleport 会经过patch创建

image.png

在438行上下可以看到

在这里有一个判断:如果是TELEPORT的元素的话,就会走Teleport的创建路线,走执行process方法

image.png

我们点进去看process方法

目录如下:
image.png

在如下的地方可以看到 TeleportImpl 里面有几个参数

主要讲的是processremove

Teleport的创建

image.png

首先我们去看process中的创建可以看到在target中我们获取到目标移动的dom节点,然后往目标元素,挂载节点。

image.png
通过mountChildren的方法挂载子节点
image.png

在最后通过disabled的判断来决定是否需要挂载到目标节点上

image.png

Teleport的更新

在判断的另一头就是更新的逻辑了

会判断两个点:一个是新节点disalbed,还有一个是旧节点wasDisabled

如果新节点disalbedtrue 旧节点disabledfalse,就把子节点移动到原本的位置。
反之则是,新节点disalbedfalse,旧节点disabledtrue,就把子节点移动到目标位置。

image.png

Teleport的删除

Teleport的删除和同样是内置组件的keep-alive 是不同的,keep-alive是将组件搬运到一个隐藏的容器里面藏起来。Teleport的删除是通过遍历 teleprot的子节点进行 unmount删除。

image.png

总结

Teleport是一个蛮不错的内置组件,Teleport 只改变了渲染的 DOM 结构,它不会影响组件间的逻辑关系。也就是说,如果 Teleport 包含了一个组件,那么该组件始终和这个使用了 teleport 的组件保持逻辑上的父子关系。传入的 props 和触发的事件也会照常工作。

这也意味着来自父组件的注入也会按预期工作,子组件将在 Vue Devtools 中嵌套在父级组件下面,而不是放在实际内容移动到的地方。

甚至而言我们也可以将 TeleportTransition 结合使用来创建一个带动画的模态框。这样子的话,就可以轻易的实现很多动画效果。

  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值