当前位置: 首页 > Web前端 > vue.js

Vue超好玩的新功能:DOM传送门

时间:2023-03-31 18:21:42 vue.js

前言自从上一篇《Vue超好玩的新特性:在CSS中引入JS变量》走红后,很多小伙伴点赞评论,所以决定把《Vue超好玩的新特性》做成一个系列。因为目前大多数Vue3文章都集中在CompositionAPI和响应式优化上。虽然这些新特性很棒,但是大家忽略了其他的新特性。或者有些文章只是简单提一下,然后写一个简单的Demo,讲解的不够详细或者不够深入。除了响应式优化和CompositionAPI之外,还有一些非常有用的新功能。所以记得关注我,带你解锁Vue3各种有趣的新特性,联想相信大家或多或少都听说过“传送门”这个词。就算没听过,至少也应该看过吧?!(00后好像几乎没见过)想想在《哆啦A梦》中经常出现的道具?时光机竹蜻蜓任意门这三个道具出现的频率很高,证明这三个道具相比于其他道具具有非常强的通用性。新版本的Vue实现了这三者之一:任意门(传送门)。传送门如果你只看到“传送门”三个字,你可能无法想象它有什么样的功能。那么我们就以《哆啦A梦》中的任意门为例:你能想一想,《哆啦A梦》中的任意门通常有哪些特点?目的地受目的地环境的影响。目的地必须特别精确。让我们解释一下:目的地:使用任何门的方法是“开门时想到目的地,否则会导致无法预测的区域”受目的地影响环境影响:如果您的目的地是极冷的地方,那么你会像下图这样:目的地一定要特别精准:据不完全统计,大雄每使用任何一扇门10次,就有3次在开门时撞到正在洗澡的静香。出现这种情况的原因是:大雄只是想看看,大雄开门的时候,他想到的目的地是“静香家”,而静香家有很多房间,但是大雄并没有给任何门一个明确的指示去到。是哪个房间,于是我就直接去了静香所在的房间(淋浴间):teleportation看看门:Destination:传送门的使用方法是给一个CSS选择器作为目的地。受目的地环境的影响:如果目的地写了一个可以继承的CSS属性(比如:color),那么传过去的DOM就会受到这个样式的影响。目的地必须非常精确:如果你给目的地一个可以匹配很多元素的选择器(比如:div),那么你只会被送到第一个匹配到的div(很可能不是你想要的那个div)。可能很多人看到这里还是头晕,这东西有什么用?我写组件把DOM封装在一起,为什么要把DOM发到别的地方?如果想在其他DOM元素中引入某个DOM,可以直接导入该组件。这不就是组件化的意义吗?对于传送门的意义,我们先来看这样一个案例:轮播图相信大家应该都不陌生了。轮播图外面有一排按钮。这排按钮可以控制面板指示点在轮播图中的位置。你可能觉得这不是很简单吗?封装一个组件,里面有这一排按钮,轮播,面板灯。完成后,定义一个位置变量,点击哪个按钮,变量会随之变化。之后面板指示点相对于轮播图片进行绝对定位,具体定位方向由该位置变量控制。那么问题来了,这一排按钮不一定在固定的位置,有可能在轮播图的左边或者右边。甚至有人说,这四个按钮可能在其他DOM元素中,离轮播很远。听了之后,你觉得没什么。我把它分成两个部分,然后就结束了。按钮是一个单独的组件,轮播和面板指示器是另一个组件:就像上图中的红框一样,这是两个不同的组件,你想放哪里就放哪里,放多远无所谓离开。当你点击按钮的时候,你只需要使用:emit,eventBus或者Vuex……一系列你能想到的组件间的通信方式来进行通信。将按钮的值传递给轮播,轮播根据传入的值来确定面板指示器的位置,这个是完全可以实现的。其实之前我们大家也一直在用这种方式实现类似的需求。但是这个实现有两个缺点:封装四个按钮的四个值好像只控制了面板的指示点,并没有控制页面其他元素的上下左右,即也就是说,它的Values只和panelcue点挂钩。如果有多个旋转木马,每个旋转木马都由一组不同的按钮控制,那么组件之间就会有很多很多的通信。一方面,在Vuex中,可以定义尽可能少的状态。另一方面,即使不使用Vuex,使用emit来派发事件,然后在父组件或祖先组件上监听事件再传递到下一层,也太麻烦了。其他一些组件通信方式也好不到哪儿去。所以在封装上,两者应该是一个整体:两者应该封装成一个组件。复用性如果项目中有多个轮播,则有一些轮播不需要面板指示点。而且不仅仅是轮播,如果还有其他div或者其他元素也需要一个可以上下左右来回切换的面板指示器和一排可以控制位置的按钮,那怎么办?先封装按钮组件,然后是面板指示器组件,然后是轮播图组件(轮播组件中引入了面板指示器组件),然后是一个没有面板指示器的轮播组件,然后是其他几个需要面板的组件……是的不觉得麻烦,复用性差,耦合度高?造成目前这种情况的原因是两者不能打包成一个组件:那么是什么阻止它们在一起呢?答案是DOM结构。如果把DOM结构封装在一起,就会变成这样:虽然为面板组件设置了绝对定位,但它并不是相对于我们希望它相对于的DOM元素。相对于特定DOM元素定位的最佳方式是成为其后代元素(当然还要在祖先元素上设置非静态定位)。但是现在仅限于这个组件,不能是其他元素的后代元素,所以DOM入口就是为了解决这个需求而诞生的!听完我是不是已经明白DOM入口的含义了?DOMPortal是将组件中的DOM元素转移到其他地方的能力。它的意义在于:从组件的角度来看,它们仍然是同一个组件,组件中定义的变量和逻辑仍然有效。从DOM结构来看,面板控制点的DOM结构变成了别人的子元素用法首先,我们根据这篇文章搭建一个支持vue3的项目《今日凌晨Vue3 beta版震撼发布,竟然公开支持脚手架项目》。当然,以上是针对你没有升级@vue/cli的情况。如果将@vue/cli升级到最新版本,就可以直接创建了:看!Vue3的预览版已经作为一个选项提供!建完工程首先要说明的是传送门是一个组件,这个组件的名字叫Teleport:那么我们要将App组件中的图片发走:那么我们只需要在app组件外包裹一个teleport组件img标签:??teleport组件必须有一个to属性,传递的是一个css选择器,相当于传送门的目的地。你可以看到img当前位于body的底部。想必底层应该使用dom.appendChild()的语法。但令人惊讶的是,img居然是在#app之外发送的。要知道这个#app就是vue代理的:原来是newVue(),然后vue挂载到这个#app元素上。现在虽然已经发到外面了,但是还是vue的吗?试一试:可以看到组件中定义的变量和逻辑对DOM元素仍然有效!那风格呢?样式也会生效吗?再试一次:这真的很好!但是我刚才传递的是一个DOM元素,我可以传递一个组件吗?传下去:还是可以的!但是当你传入一个字符串进行teleport时,它的底层会调用document.querySelector()方法。该方法只会匹配第一个匹配选择器的元素,所以尽量使用更精确的选择器,比如:'#id'。除了to属性,teleport还支持传入一个disabled属性,需要传入一个布尔值。有时候我们需要根据某些条件动态判断是否需要传输,但是这种情况不适合用v-if来处理,因为如果v-if为false,虽然不会传输,但是同时它不会出现在原来的位置,所以disabled这个时候就派上用场了。disabled按字面意思就是使无效,默认为false,即不使teleport无效。换句话说,就是生效的意思,那么我们给它赋一个真值,让瞬移失效:可以看到,它并没有传送到本体,而是停留在原来的位置,仔细看,会还是发现了这样一条评论:证明不是你没有写teleport,而是因为它被禁用了!灵感估计是随便猜的,你们都知道灵感是从哪里来的。学过React的朋友似乎觉得这个瞬移好像有点眼熟。没错,它的灵感来自于React的createPortal,但是Vue的设计更符合组件化的直觉。对比一下:其实Portal这个词就是门户的意思:当然译者没有翻译成门户,但是意思差不多。关键是这个词是名词。按理说,组件名应该是一个名词。据说一开始游玉玺给它起的名字叫Portal,不过听说Chrome浏览器正在研究一个组件。如果成功,它可能会部署在浏览器上。为了避免以后可能发生的冲突,改名为动词teleport就行了!