当前位置: 首页 > 编程笔记 >

Vue.js之slot深度复制详解

於鸿羲
2023-03-14
本文向大家介绍Vue.js之slot深度复制详解,包括了Vue.js之slot深度复制详解的使用技巧和注意事项,需要的朋友参考一下

前言

在Vue中,slot是一个很有用的特性,可以用来向组件内部插入一些内容。slot就是“插槽”的意思,用大白话说就是:定义组件的时候留几个口子,由用户来决定插入的内容。

例如我们定义一个组件MyComponent,其包含一个slot:

Vue.component('MyComponent', {
 template: `
 <div>
  <slot></slot>
 </div>
 `
})

当调用<MyComponent>123</MyComponent>时,会渲染为如下DOM结构:

<div>
 123
</div>

现在又有新需求了,我们希望调用<MyComponent>123</MyComponent>时,渲染出这样的DOM结构:

<div>
 123
 123
</div>

看起来很容易实现,即再为MyComponent添加一个slot:

Vue.component('MyComponent', {
 template: `
 <div>
  <slot></slot>
  <slot></slot>
 </div>
 `
})

渲染出的结构也确实如你所愿,唯一美中不足的是控制台有一个小小的Warning:

Duplicate presence of slot "default" found in the same render tree

如果你不是强迫症患者,这时候你可以收工安心回家睡觉了。直到有一天你的同事向你抱怨,为什么向MyComponent插入一个自定义组件会渲染不出来?

例如有一自定义组件MyComponent2:

Vue.component('MyComponent2', {
 template: `
 <div>456</div>
 `
})

当调用<MyComponent><MyComponent2></MyComponent2></MyComponent>时,预期渲染为如下DOM结构:

<div>
 <div>456</div>
 <div>456</div>
</div>

为什么不能正常工作呢?估计是前面的那个Warning搞得鬼,通过查询发现在Vue 2.0中不允许有重名的slot:

重名的 Slots 移除

同一模板中的重名 已经弃用。当一个 slot 已经被渲染过了,那么就不能在同一模板其它地方被再次渲染了。如果要在不同位置渲染同一内容,可一用 prop 来传递。

文档中提示可以用props来实现,然而在我的用例中显然是不合适的。经过搜索后,最靠谱的方法是手写render函数,将slot中的内容复制到其他的位置。

将之前的MyComponent改为render函数的方式定义:

Vue.component('MyComponent', {
 render (createElement) {
 return createElement('div', [
  ...this.$slots.default,
  ...this.$slots.default
 ])
 }
})

在上面的定义中我们插入了两个this.$slots.default,测试下能不能正常工作。然而并没有什么卵用,Vue文档在render函数这一章有以下说明:

VNodes 必须唯一

所有组件树中的 VNodes 必须唯一

这意味着我们不能简单地在不同位置引用this.$slots.default,必须对slot进行深度复制。深度复制的函数如下:

function deepClone(vnodes, createElement) {
 function cloneVNode (vnode) {
 const clonedChildren = vnode.children && vnode.children.map(vnode => cloneVNode(vnode));
 const cloned = createElement(vnode.tag, vnode.data, clonedChildren);
 cloned.text = vnode.text;
 cloned.isComment = vnode.isComment;
 cloned.componentOptions = vnode.componentOptions;
 cloned.elm = vnode.elm;
 cloned.context = vnode.context;
 cloned.ns = vnode.ns;
 cloned.isStatic = vnode.isStatic;
 cloned.key = vnode.key;
 return cloned;
 }
 const clonedVNodes = vnodes.map(vnode => cloneVNode(vnode))
 return clonedVNodes;
}

上面的核心函数就是cloneVNode() ,它递归地创建VNode,实现深度复制。VNode的属性很多,我并不了解哪些是关键属性,只是参照着Vue的源码一并地复制过来。

基于以上函数,我们更改MyComponent的定义:

Vue.component('MyComponent', {
 render (createElement) {
 return createElement('div', [
  ...this.$slots.default,
  ...deepClone(this.$slots.default, createElement)
 ])
 }
})

测试,一切正常。

总结

在Vue 1.0中重名的slots并不会出现什么问题,不知道为什么在2.0中取消了这个功能。我听说React提供了复制Element的标准函数,希望Vue也能提供这个函数,免得大家踩坑。以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

 类似资料:
  • 本文向大家介绍Vue.js中组件中的slot实例详解,包括了Vue.js中组件中的slot实例详解的使用技巧和注意事项,需要的朋友参考一下 Vue组件中的slot slot 可以实现在已经定义的组件中添加内容,组件会接收内容并输出,假如有一个组件person,它的里面包含的是个人信息,如下面这样 在应用的时候,当然希望这里面可以是灵活变化的,所以这就需要用到slot了 首先要做的事情就是创建这样一

  • 问题内容: 我正在尝试复制嵌套列表,但是 不 使用该函数不知道该如何做。 我用了: 和 但事实证明,它们全都是浅表。 有什么提示吗? 问题答案: 我的模拟输入: 策略:遍历传入对象的每个元素,递归地下降到也可迭代的元素中,并创建相同类型的新对象。 无论它是全面的还是没有错误的,我都不会提出任何主张[1](不要传递引用自己的对象!),但是应该让您入门。 [1]真的!这里的重点是演示,而不是涵盖所有可

  • 问题内容: 我有一个带有Node的图类,其中每个Node可以连接到其他节点: 我想复制整个图。第一次尝试,我尝试制作一个类似以下的复制构造函数: 因此,深度复制图形将是: 但这不起作用,因为这破坏了节点之间的连接关系。我想知道是否有人建议以一种简单的方式做到这一点?谢谢。 问题答案: 问题是您需要复制节点的身份,而不仅仅是节点的值。具体来说,当您复制某个节点时,您需要处理其所指节点的身份。这意味着

  • 本文向大家介绍深入理解vue中的slot与slot-scope,包括了深入理解vue中的slot与slot-scope的使用技巧和注意事项,需要的朋友参考一下 写在前面 vue中关于插槽的文档说明很短,语言又写的很凝练,再加上其和methods,data,computed等常用选项使用频率、使用先后上的差别,这就有可能造成初次接触插槽的开发者容易产生“算了吧,回头再学,反正已经可以写基础组件了”,

  • 本文向大家介绍php5对象复制、clone、浅复制与深复制实例详解,包括了php5对象复制、clone、浅复制与深复制实例详解的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了php5对象复制、clone、浅复制与深复制。分享给大家供大家参考,具体如下: 对象复制的由来 为什么对象会有“复制”这个概念,这与PHP5中对象的传值方式是密切相关的,让我们看看下面这段简单的代码 PHP代码 这段代

  • 问题内容: 我想知道是否需要避免在创建一个带有嵌入式对象数组的简单对象时复制对对象的引用。情况如下:我有一个接受JSON并应用一些逻辑然后将对象存储在其中的服务器D B。可以说,我的表单用于保存DB中的团队。服务器接受team作为json。团队有一个TeamMember对象数组,我的表单有一个简单的字段来输入团队成员信息并将其添加到团队teamMembers数组中。现在这是一个问题,当我向数组列表