乐趣区

关于前端:Vue超好玩的新特性DOM传送门

前言

自从上一篇《Vue 超好玩的新个性:在 CSS 中引入 JS 变量》大火之后,很多小伙伴纷纷点赞留言,所以我决定将《Vue 超好玩的新个性》做成一个系列。

因为目前大多数的 Vue 3 文章重点都在讲 Composition API响应式的优化,尽管这些新个性很棒,但大家都把其余的新个性给疏忽掉了。或者有的文章只是简略的提了一嘴,而后写个简略的 Demo,解说的不够粗疏也不够深刻。

除了 响应式的优化 Composition API之外还有一些新个性十分的实用。所以记得关注我,带你解锁 Vue 3 各种好玩的新个性。

联想

置信大家或多或少都据说过 传送门 这个词。就算没有据说过,那么至多也应该看过《 哆啦 A 梦》吧!(00 后如同根本都没看过)

想一想在《哆啦 A 梦》中经常出现的道具都有什么?

  • 时光机
  • 竹蜻蜓
  • 任意门

这三个道具经常出现,就证实这仨相比于其余道具来说,具备十分弱小的通用性。

新版 Vue 就实现了这三个当中的:任意门 ( 传送门 )。

传送门


如果单单看到 传送门 这三个字的话,有可能无奈联想出这到底是一个怎么的性能。

所以咱们就用《哆啦 A 梦》中的任意门来进行举例:

能够想一想《哆啦 A 梦》中的任意门通常都有哪些特点?

  • 目的地
  • 受目的地环境的影响
  • 目的地必须特地准确

咱们来解释一下:

  • 目的地:任意门的应用办法是 ” 在打开门时要想着目的地,否则将通向无奈预知的地区 ”
  • 受目的地环境的影响:如果你的目的地是一个极寒之地,那么你就会像下图这样:

  • 目的地必须特地准确 :据不齐全统计,大雄每 10 次应用任意门,就会有 3 次打开门撞见静香在洗澡。之所以呈现这种状况的起因就是: 大雄就是想看 大雄打开门的时候想的目的地是 ” 静香家 ”,而静香家有很多房间,但大雄并没有给任意门一个明确的批示到底去的是哪个房间,于是就间接去了静香所在的房间( 淋浴间 ):

<center>(只是举个例子不要钻牛角尖)</center>

Vue 中的传送门

那么再把这几个个性放在 Vue 的传送门中来看看:

  • 目的地:传送门的应用办法是给一个 CSS 选择器来作为目的地。
  • 受目的地环境的影响 :如果目的地写了一个能够继承的那种 CSS 属性( 如:color ),那么被传送过来的 DOM 就会受到这个款式的影响。
  • 目的地必须特地准确 :如果你给目的地了一个能够匹配很多元素的选择器( 如: div ),那么只会给你传送到第一个匹配到的 div 外面去 ( 很有可能不是你想去的那个 div )。

可能不少人看到这里还是晕晕乎乎的,这玩意到底有啥用?我写组件不就是为了把 DOM 封装在一起,干嘛要让 DOM 传送到别的中央去呢?

要是想在别的 DOM 元素中引入某个 DOM,间接引入这个组件不就好了嘛,这不正是组件化的意义所在吗?

传送门的意义

来看这样一个案例:

置信大家应该对轮播图都很相熟了,在这个轮播图之外有一排按钮,这排按钮能够管制轮播图外部的 面板批示点 的地位。

你可能会感觉这不很简略吗?封装一个组件,外面蕴含了这一排按钮、轮播图、面板指示灯。完了再定义一个地位变量,点击哪个按钮,变量就会随之变动。

之后 面板批示点 绝对于轮播图来进行相对定位,具体定位在哪个方向就靠这个地位变量来管制了。

那么问题来了,这一排按钮并不一定是在一个固定的地位,有可能在这个轮播图的右边,也有可能在右侧。
甚至说这四个按钮可能在别的 DOM 元素内,间隔轮播图很远。

你听了当前感觉这也没啥啊,我把这个拆分成两个组件不就完了。按钮是独自的一个组件,轮播图和 面板批示点 又是另一个组件:

就像上图红框里的那样,这是两个不同的组件,你爱把他俩放哪就放哪,放多远都没关系。

当点击按钮的时候只须要用:emit、eventBus 或者 Vuex…等一系列你能想到的组件间通信形式进行通信就能够了。
把按钮的值传给轮播图,而后轮播图依据传进来的值来决定 面板批示点 的地位。

这么做齐全能够实现,事实上以前咱们大家始终都是这样实现相似需要的。

但这种实现有两方面的弊病:

封装性

这四个按钮的四个值仿佛只会管制这个 面板批示点 ,它并不会去管制页面上其余的什么元素的上下左右,也就是说它的值只关联到 面板批示点

而且如果要是有好几个轮播图的话,每个轮播图都是靠不同的一组按钮来管制,那么就会存在很多很多的组件间通信。

一方面在 Vuex 中能尽量少定义状态就尽量少定义。另一方面即便没用 Vuex,用 emit 派发事件,而后再在父组件或先人组件上监听事件再向下层层传递也显得过于麻烦,其余的一些组件通信形式也没好到哪去。

所以从封装性上来讲,它俩才应该是一个整体:

它们两个才应该被封装到一个组件当中去。

可复用性

如果我的项目中不止有这一个轮播图,还有一些不须要 面板批示点 的轮播图。而且不只是轮播图,如果还有别的 div 或者什么其余元素也须要一个能上下左右来回切换的 面板批示点 和一排能够管制地位的按钮,那你应该怎么做?

先封装按钮组件、再面板批示点组件、再轮播图组件 ( 轮播组件中引入面板批示组件)、而后再来一个不带面板批示的轮播组件、再来几个须要面板的其余组件……

是不是感觉很麻烦、可复用性差而且耦合水平还高?之所以会呈现目前这种情景,正是因为不能把他俩封装成一个组件而导致的:

那么到底是什么起因妨碍了它们两个在一起呢?

答案就是 DOM 构造

DOM 构造

如果把他俩封装到一起去会变成这样:

<template>
  <div>
    <!-- 按钮组件 -->
      <Btns/>
    
    <!-- 面板组件 -->
    <Points/>
  </div>
</template>

<style>
  .points {position: absolute;}
</style>

虽说为面板组件设置了相对定位,但它绝对的并不是咱们想要让它绝对的那个 DOM 元素。想要绝对具体某个 DOM 元素定位最好的形式就是成为它的后辈元素 ( 当然还要在先人元素上设置非 static 定位 )。

但当初 <Points> 被限度在了这个组件中,没方法成为他人的后辈元素,所以 DOM 传送门就是为了解决这个需要而诞生的!

听我讲到这里是不是曾经有点明确过去 DOM 传送门的意义了呢?
DOM 传送门就是能把组件中的 DOM 元素传送到别的中央去。

它的意义就在于:

  • 从组件角度来看,它俩仍旧是同一个组件,组件内定义的变量以及逻辑仍然失效
  • 从 DOM 构造来看,面板控制点的 DOM 构造曾经成为了他人的子元素了

用法

首先咱们先依据《今日凌晨 Vue3 beta 版震撼公布,居然公开反对脚手架我的项目》这篇文章来搭建一个反对 vue 3 的一个我的项目。

当然了下面这种是针对你没有降级 @vue/cli 的这么一种状况,如果你把 @vue/cli 降级到最新版的话间接就能够创立:

看!曾经能够抉择 Vue 3 的预览版了!

建完我的项目后要先阐明的一点是 传送门 是一个组件,这个组件的名字叫做 Teleport :

而后咱们想把 App 组件内的图片送走:

那么咱们只需在 img 标签的外侧包裹一个 teleport 组件:

⚠️ teleport 组件必须有一个 to 属性,传递的是一个 css 选择器,相当于传送门的目的地


能够看到 img 目前位于 body 内的最底部,想必底层应该是使用了 dom.appendChild() 这样的语法。

不过令人诧异的是:img 竟然被传送到了 #app 的里面去,要晓得 vue 代理的可就是这个 #app:

以前是 new Vue(),而后 vue 挂载到这个 #app 元素上。虽说当初曾经被传送到了里面去,那它还归不归 vue 管呢?来试一下:


能够看到组件外面定义的变量以及逻辑对该 DOM 元素仍然失效!

那样式呢?款式也会失效吗?再来试一下:


这可真是太棒了!

不过方才传的是 DOM 元素,可不可以传组件呢?咱们这就来传一下 <HelloWorld/> :



还是没故障!

不过当你给 teleport 传入字符串的时候,它的底层会调用 document.querySelector() 这个办法。这个办法只会匹配到第一个合乎选择器的元素,所以尽量要应用准确一点的选择器,如:’#id’。

除了 to 属性外,teleport 还反对传入一个 disabled 属性,该属性须要传入一个布尔值。

有时咱们须要依据某种条件来动静的判断是否须要传送,但这种状况并不适宜用 v-if 来解决,因为如果 v-if 为 false 的话,尽管说不会被传送了,但同时它也不会呈现在它本来的地位上了,所以这个时候 disabled 就派上用场了。

从字面意义上来看 disabled 是使有效的意思,它默认是 false,也就是说 不使 teleport 有效 。换句话说就是失效的意思,那咱们来给它一个 true 值让 teleport 不失效:


能够看到它并没有被传送到 body 里去,而是还待在它原来的地位,而且认真看的话还会发现这样一段正文:

以此来证实不是你没写 teleport,而是因为它被禁用了!

灵感


预计轻易一猜你们都晓得这是哪里来的灵感,学过 React 的小伙伴们仿佛会感觉这个 teleport 如同有点似曾相识。

没错,它的灵感就来自 React 的 createPortal,然而 Vue 设计的更合乎组件化的直觉,咱们来比照一下:

其实 Portal 这个单词才是 传送门 的意思:

当然翻译器并没有翻译成传送门,然而意思其实也差不多啦。要害是这个单词它是一个名词,按情理来讲组件名称应该是名词才对。

据说尤雨溪一开始是把它命名为 Portal 的,然而据说 Chrome 浏览器正在钻研一个 <portal> 组件,如果胜利了的话可能会在浏览器上部署,为了防止将来可能会产生的抵触,就把它改名为 teleport 这个动词啦!

结语

怎么样,这个新个性是不是既实用又好玩呢?

如果还是感觉意犹未尽的话能够看看往期精彩文章:

  • 《Vue 超好玩的新个性:在 CSS 中引入 JS 变量》
  • 《不依赖任何库打造属于本人的可视化数据地图》
  • 《在 Vue 我的项目中应用 React 超火的 CSS-in-JS 库: styled-components》
  • 《终于轮到 Vue 来带给 React 灵感了?》
  • 《Vue3 在 IOS 下的一个小坑》
  • 《2020 要用 immer 来代替 immutable 优化你的 React 我的项目》
  • 《来自《React Hooks 与 Immutable》小册作者 ’ 神三元 ’ 的灵魂拷问》
  • 《好消息,Vue3 官网文档出中文版的啦!》
  • 《新版 vue-router 的 hooks 用法》
  • [《[译]Vue 3:2020 年中状态更新》](https://juejin.im/post/684790…
  • [《[译]React 17 终于公布 RC 版本了 官网竟说 17 是个过渡版!》](https://juejin.im/post/685973…
  • [《[译]尤雨溪:Vue3 的设计过程》](https://juejin.im/post/684490…
  • 《Node 之父重构的 Deno 终于公布了,它终究会取代 Node 吗?》
  • 《今日凌晨 Vue3 beta 版震撼公布,居然公开反对脚手架我的项目!》
退出移动版