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 相关文章推荐
使用prototype.js 的时候应该特别注意的几个问题.
Apr 12 Javascript
JavaScript 嵌套函数指向this对象错误的解决方法
Mar 15 Javascript
JS仿flash上传头像效果实现代码
Jul 18 Javascript
仿jQuery的siblings效果的js代码
Aug 09 Javascript
jquery实现奇偶行赋值不同css值
Feb 17 Javascript
js清除input中type等于file的值域(示例代码)
Dec 24 Javascript
节点的插入之append()和appendTo()的用法介绍
Jan 13 Javascript
jQuery如何防止这种冒泡事件发生
Feb 27 Javascript
jQuery中delegate()方法的用法详解
Oct 13 Javascript
js闭包用法实例详解
Dec 13 Javascript
页面内锚点定位及跳转方法总结(推荐)
Apr 24 Javascript
javascript 使用sleep函数的常见方法详解
Apr 26 Javascript
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
使用MaxMind 根据IP地址对访问者定位
2006/10/09 PHP
PHP语法速查表
2007/01/02 PHP
PHP教程 变量定义
2009/10/23 PHP
解析php session_set_save_handler 函数的用法(mysql)
2013/06/29 PHP
PHP 魔术变量和魔术函数详解
2015/02/25 PHP
PHP面向对象程序设计OOP继承用法入门示例
2016/12/27 PHP
Laravel框架用户登陆身份验证实现方法详解
2017/09/14 PHP
PHP错误提示It is not safe to rely on the system……的解决方法
2019/03/25 PHP
javascript multibox 全选
2009/03/22 Javascript
JS 两个字符串时间的天数差计算
2013/08/25 Javascript
JS测试显示屏分辨率以及屏幕尺寸的方法
2013/11/22 Javascript
js 点击页面其他地方关闭弹出层(示例代码)
2013/12/24 Javascript
jQuery使用之标记元素属性用法实例
2015/01/19 Javascript
JS简单生成两个数字之间随机数的方法
2016/08/03 Javascript
JavaScript基础——使用Canvas绘图
2016/11/02 Javascript
微信小程序购物商城系统开发系列-目录结构介绍
2016/11/21 Javascript
jQuery实现复制到粘贴板功能
2017/02/11 Javascript
jquery实现左右滑动式轮播图
2017/03/02 Javascript
jquery插件canvaspercent.js实现百分比圆饼效果
2017/07/18 jQuery
react-native使用react-navigation进行页面跳转导航的示例
2017/09/07 Javascript
no-vnc和node.js实现web远程桌面的完整步骤
2019/08/11 Javascript
工作中常用js功能汇总
2020/11/07 Javascript
vue+iview实现分页及查询功能
2020/11/17 Vue.js
vue的hash值原理也是table切换实例代码
2020/12/14 Vue.js
python计算牛顿迭代多项式实例分析
2015/05/07 Python
Python中shapefile转换geojson的示例
2019/01/03 Python
Python2与Python3的区别实例总结
2019/04/17 Python
Python中filter与lambda的结合使用详解
2019/12/24 Python
python 双循环遍历list 变量判断代码
2020/05/04 Python
Python matplotlib 绘制双Y轴曲线图的示例代码
2020/06/12 Python
5款实用的python 工具推荐
2020/10/13 Python
HTML5之多线程(Web Worker)
2019/01/02 HTML / CSS
优秀演讲稿范文
2013/12/29 职场文书
打架检讨书2000字
2014/02/22 职场文书
2014小学年度工作总结
2014/12/20 职场文书
大学军训通讯稿
2015/07/18 职场文书