Vue.js之slot深度复制详解


Posted in Javascript onMarch 10, 2017

前言

在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也能提供这个函数,免得大家踩坑。以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Javascript 相关文章推荐
jquery获得下拉框值的代码
Aug 13 Javascript
Javascript字符串浏览器兼容问题分析
Dec 01 Javascript
jquery单选框radio绑定click事件实现方法
Jan 14 Javascript
JavaScript高级教程5.6之基本包装类型(详细)
Nov 23 Javascript
特殊日期提示功能的实现方法
Jun 16 Javascript
jQuery EasyUI Panel面板组件使用详解
Feb 28 Javascript
JavaScript变量作用域_动力节点Java学院整理
Jun 27 Javascript
全新打包工具parcel零配置vue开发脚手架
Jan 11 Javascript
LayUI表格批量删除方法
Aug 15 Javascript
Layui 带多选框表格监听事件以及按钮自动点击写法实例
Sep 02 Javascript
JavaScript字符串转数字的简单实现方法
Nov 27 Javascript
vue使用Google Recaptcha验证的实现示例
Aug 23 Vue.js
JS实现的自动打字效果示例
Mar 10 #Javascript
jquery实现的table排序功能示例
Mar 10 #Javascript
微信小程序 向左滑动删除功能的实现
Mar 10 #Javascript
常用的js方法合集
Mar 10 #Javascript
利用Angular+Angular-Ui实现分页(代码加简单)
Mar 10 #Javascript
JS中利用localStorage防止页面动态添加数据刷新后数据丢失
Mar 10 #Javascript
C#微信小程序服务端获取用户解密信息实例代码
Mar 10 #Javascript
You might like
基于php iconv函数的使用详解
2013/06/09 PHP
使用PHP编写发红包程序
2015/07/22 PHP
PHP编程快速实现数组去重的方法详解
2017/07/22 PHP
按下Enter焦点移至下一个控件的实现js代码
2013/12/11 Javascript
Javascript 创建类并动态添加属性及方法的简单实现
2016/10/20 Javascript
微信小程序 slider的简单实例
2017/04/19 Javascript
详解ElementUI之表单验证、数据绑定、路由跳转
2017/06/21 Javascript
javascript中神奇的 Date对象小结
2017/10/12 Javascript
vue-router 路由基础的详解
2017/10/17 Javascript
结合mint-ui移动端下拉加载实践方法总结
2017/11/08 Javascript
JS实现的RC4加密算法示例
2018/08/16 Javascript
JavaScript Canvas实现验证码
2020/08/02 Javascript
关于RxJS Subject的学习笔记
2018/12/05 Javascript
vue element中axios下载文件(后端Python)
2019/05/10 Javascript
如何测量vue应用运行时的性能
2019/06/21 Javascript
js如何获取访问IP、地区、当前操作浏览器
2019/07/23 Javascript
mpvue微信小程序开发之实现一个弹幕评论
2019/11/24 Javascript
vue 动态生成拓扑图的示例
2021/01/03 Vue.js
[01:48:04]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Elephant BO3 第一场 2月7日
2021/03/11 DOTA
Python批量修改文件后缀的方法
2014/01/26 Python
PyQt5高级界面控件之QTableWidget的具体使用方法
2020/02/23 Python
学python爬虫能做什么
2020/07/29 Python
CSS3——齿轮转动关键代码
2013/05/02 HTML / CSS
canvas实现滑动验证的实现示例
2020/08/11 HTML / CSS
英国豪华文具和皮具配件经典老品牌:Smythson(斯迈森)
2018/04/19 全球购物
护士长竞聘演讲稿
2014/04/30 职场文书
公司采购主管岗位职责
2014/06/17 职场文书
大学生万能检讨书范例
2014/10/04 职场文书
大学生档案自我鉴定(2篇)
2014/10/14 职场文书
优秀班主任申报材料
2014/12/16 职场文书
海上钢琴师观后感
2015/06/03 职场文书
2016年大学生寒假社会实践心得体会
2015/10/09 职场文书
2016国培学习心得体会
2016/01/08 职场文书
Nginx+Tomcat实现负载均衡、动静分离的原理解析
2021/03/31 Servers
纯 CSS 自定义多行省略的问题(从原理到实现)
2021/11/11 HTML / CSS
国际最新研究在陨石中发现DNA主要成分 或由陨石带来地球
2022/04/29 数码科技