Vue.js之slot深度复制详解

 更新时间:2017年03月10日 14:18:25   作者:jingsam  
这篇文章主要介绍了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也能提供这个函数,免得大家踩坑。以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

相关文章

  • Vue3中嵌套路由和编程式路由的实现

    Vue3中嵌套路由和编程式路由的实现

    Vue Router在Vue.js的核心库上提供了路由的功能,使得我们可以在单页应用中实现页面的切换、跳转和参数传递等功能,本文主要介绍了Vue3中嵌套路由和编程式路由的实现,感兴趣的可以了解一下
    2023-12-12
  • 利用vue控制元素的显示与隐藏

    利用vue控制元素的显示与隐藏

    大家都知道在vue中我们可以使用v-if或者v-show去做隐藏显示,下面这篇文章主要给大家介绍了关于如何利用vue控制元素的显示与隐藏的相关资料,需要的朋友可以参考下
    2022-12-12
  • vue.js单文件组件中非父子组件的传值实例

    vue.js单文件组件中非父子组件的传值实例

    今天小编就为大家分享一篇vue.js单文件组件中非父子组件的传值实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-09-09
  • Vue实现简单基础的图片裁剪功能

    Vue实现简单基础的图片裁剪功能

    这篇文章主要为大家详细介绍了如何利用Vue2实现简单基础的图片裁剪功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下
    2022-09-09
  • vue3前端导出excel表格的两种实现方法

    vue3前端导出excel表格的两种实现方法

    这篇文章主要给大家介绍了关于vue3前端导出excel表格的两种实现方法,文中通过代码以及图文介绍的非常详细,对大家学习或者使用vue3具有一定的参考借鉴价值,需要的朋友可以参考下
    2024-11-11
  • Vue.js中v-for指令的用法介绍

    Vue.js中v-for指令的用法介绍

    这篇文章介绍了Vue.js中v-for指令的用法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03
  • 浅析vue cli3 封装Svgicon组件正确姿势(推荐)

    浅析vue cli3 封装Svgicon组件正确姿势(推荐)

    这篇文章主要介绍了vue cli3 封装Svgicon组件正确姿势,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • vue webuploader 文件上传组件开发

    vue webuploader 文件上传组件开发

    本篇文章主要介绍了vue webuploader 文件上传组件开发,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-09-09
  • vue 点击按钮实现动态挂载子组件的方法

    vue 点击按钮实现动态挂载子组件的方法

    今天小编就为大家分享一篇vue 点击按钮实现动态挂载子组件的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-09-09
  • Vue3+Element-Plus实现左侧菜单折叠与展开功能示例

    Vue3+Element-Plus实现左侧菜单折叠与展开功能示例

    本文主要介绍了Vue3+Element-Plus实现左侧菜单折叠与展开功能示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04

最新评论