从组件封装看Vue的作用域插槽的实现


Posted in Javascript onFebruary 12, 2019

作用域插槽不是那么直观的一个概念。Vue文档使用了一段描述性的话来解释作用域插槽:

有的时候你希望提供的组件带有一个可从子组件获取数据的可复用的插槽
……
但是在我们应用的某些部分,我们希望每个独立的待办项渲染出和 todo.text 不太一样的东西。这也是作用域插槽的用武之地。

但在我看来,至少是第一次读到的时候,这段话相当不好理解。插槽不是分发内容到子组件吗,为什么还要从子组件中获取数据?不是已经有了通过emit事件的方法从子组件向父组件传递数据吗,为什么需要它?作用域插槽到底是来干嘛的?……

在浏览了不少博客、自己思考“如果不这么做,就会怎么样”再动手实践之后,作用域插槽的含义才逐渐明了。其实作用域插槽提供了一种封装可复用组件的新思路。下面我会从最简单的例子开始。

简单的展示列表

现在我们做一个纯展示用途的列表组件,如下图所示:

从组件封装看Vue的作用域插槽的实现

第一个例子先用slot来分发内容

<template>
 <div class="list">
  <div class="list-title">
   <slot name="title"></slot>
  </div>
  <div class="list-content">
   <slot name="content"></slot>
  </div>
 </div>
</template>

<script>
 export default {
  name: "MyList"
 }
</script>

在父组件中使用MyList

<template>
 <MyList>
  <span slot="title">title</span>
  <ul slot="content">
   <li v-for="item in listData">{{item}}</li>
  </ul>
 </MyList>
</template>

<script>
 import myList from './List.vue';
 export default {
  name: 'HelloWorld',
  components: {
   'MyList': myList
  },
  data() {
   return {
    listData: [
      '列表项1',
      '列表项2',
      '列表项3'
    ]
   }
  }
 }
</script>

省略了其中的样式代码,结果如图所示

从组件封装看Vue的作用域插槽的实现

满足了基本的需求,但是作为组件的使用者,这样的一个组件会让我觉得非常麻烦,content中循环的逻辑还需要我自己动手来写,这样的使用毫无便利性。于是有了下面第二个版本

使用prop来传递数据

因为考虑到列表的内容总是一个数组,我把循环结构写进了组件中

列表组件第二版:

<template>
 <div class="list">
  <div class="list-title">{{title}}</div>
  <ul class="list-content">
   <li v-for="(item ,index) in content" :key="index">{{item}}</li>
  </ul>
 </div>
</template>

<script>
 export default {
  name: "MyList",
  props: {
   title: {
    type: String,
    required: true
   },
   content: {
    type: Array,
    required: true
   }
  }
 }
</script>

使用起来也非常方便,只需通过prop将数据传入组件中

<template>
 <div>
  <MyList title="标题1" :content="listData"></MyList>
  <MyList title="标题2" :content="newListData"></MyList>
 </div>
</template>

<script>
 import myList from './List.vue';
 export default {
  name: 'HelloWorld',
  components: {
   'MyList': myList
  },
  data() {
   return {
    listData: [
      '列表项1',
      '列表项2',
      '列表项3'
    ],
    newListData: [
      '新的列表项1',
      '新的列表项2',
      '新的列表项3'
    ],
   }
  }
 }
</script>

改进之后,每当我使用组件只需一行代码,大大简化了工作量

从组件封装看Vue的作用域插槽的实现

易用性的需求也满足了,但现在又有了新的问题,组件的拓展性不好!每次只能生成相同结构的列表,一旦业务需求发生了变化,组件就不再适用了。比如我现在有了新的需求,在一个列表的每个列表项前加入了一个小logo,我总不可能又写一个新的组件来适应需求的变化吧?假如需要更多的定制化场景呢?

作用域插槽

这里就有了第三版的列表组件,使用作用域插槽将子组件中的数据传递出去 

<template>
 <div class="list">
  <div class="list-title">{{title}}</div>
  <ul class="list-content">
   <li v-for="(item ,index) in content" :key="index">
    <!--这里将content中的每一项数据绑定到slot的item变量上,在父组件中可以获取到item变量-->
    <slot :item="item">{{item}}</slot>
   </li>
  </ul>
 </div>
</template>

使用组件时,将业务所需的content模板传入

<template>
 <div>
  <MyList title="标题1" :content="listData1"></MyList>
  <MyList title="标题2" :content="listData2">
   <template slot-scope="scope">
    <img :src="scope.item.img" width="20">
    <span>{{scope.item.text}}</span>
   </template>
  </MyList>
  <MyList title="标题3" :content="listData3">
   <template slot-scope="scope">
    <b>{{scope.item.prefix ? '有前缀' : '无前缀'}}</b>
    <span>{{scope.item.text}}</span>
    <span>{{scope.item.remark}}</span>
   </template>
  </MyList>
 </div>
</template>

<script>
 import myList from './List.vue';

 export default {
  name: 'HelloWorld',
  components: {
   'MyList': myList
  },
  data() {
   return {
    listData1: [
     '列表项1',
     '列表项2',
     '列表项3'
    ],
    listData2: [
     {text: '第二个列表的列表项1', img: 'example.png'},
     {text: '第二个列表的列表项2', img: 'example.png'},
     {text: '第二个列表的列表项3', img: 'example.png'}
    ],
    listData3: [
     {text: '第三个列表的列表项1', prefix: true, remark: '附加的备注1'},
     {text: '第三个列表的列表项2', prefix: false, remark: '附加的备注2'},
     {text: '第三个列表的列表项3', prefix: true, remark: '附加的备注3'}
    ],
   }
  }
 }
</script>

实现了定制化的列表

从组件封装看Vue的作用域插槽的实现

再回到开始的问题,作用域插槽到底是干嘛用的?很显然,它的作用就如官网所说的一样:将组件的数据暴露出去。而这么做,给了组件的使用者根据数据定制模板的机会,组件不再是写死成一种特定的结构。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
关闭浏览器时提示onbeforeunload事件
Dec 25 Javascript
用JS在浏览器中创建下载文件
Mar 05 Javascript
jQuery javascript获得网页的高度与宽度的实现代码
Apr 26 Javascript
canvas红包照片实例分享
Feb 28 Javascript
JavaScript实现左右下拉框动态增删示例
Mar 09 Javascript
vue实现登陆登出的实现示例
Sep 15 Javascript
详解JavaScript中的强制类型转换
Apr 15 Javascript
浅谈layui使用模板引擎动态渲染元素要注意的问题
Sep 14 Javascript
vue中实现高德定位功能
Dec 03 Javascript
Vue项目页面跳转时浏览器窗口上方显示进度条功能
Mar 26 Javascript
功能完善的小程序日历组件的实现
Mar 31 Javascript
Node.js 中判断一个文件是否存在
Aug 24 Javascript
用element的upload组件实现多图片上传和压缩的示例代码
Feb 12 #Javascript
PostgreSQL Node.js实现函数计算方法示例
Feb 12 #Javascript
Vue 动态组件与 v-once 指令的实现
Feb 12 #Javascript
在微信小程序中保存网络图片
Feb 12 #Javascript
VUE中使用MUI方法
Feb 12 #Javascript
如何利用ES6进行Promise封装总结
Feb 11 #Javascript
在vue项目中引入vue-beauty操作方法
Feb 11 #Javascript
You might like
php中如何防止表单的重复提交
2013/08/02 PHP
PHP实现的抓取小说网站内容功能示例
2019/06/27 PHP
Laravel 集成微信用户登录和绑定的实现
2019/12/27 PHP
什么是JavaScript
2009/08/13 Javascript
在网页中使用document.write时遭遇的奇怪问题
2010/08/24 Javascript
JavaScript不使用prototype和new实现继承机制
2014/12/29 Javascript
js用拖动滑块来控制图片大小的方法
2015/02/27 Javascript
JS更改select内option属性的方法
2015/10/14 Javascript
jQuery实现彩带延伸效果的网页加载条loading动画
2015/10/29 Javascript
JavaScript操作表单实例讲解(上)
2016/06/20 Javascript
Angular2使用Angular-CLI快速搭建工程(二)
2017/05/21 Javascript
js导出Excel表格超出26位英文字符的解决方法ES6
2017/11/15 Javascript
20行JS代码实现粘贴板复制功能
2018/02/06 Javascript
vue组件之间通信方式实例总结【8种方式】
2019/02/22 Javascript
详解在React-Native中持久化redux数据
2019/05/22 Javascript
JS利用prototype给类添加方法操作详解
2019/06/21 Javascript
jquery实现异步文件上传ajaxfileupload.js
2020/10/23 jQuery
JavaScript实现鼠标经过表格某行时此行变色
2020/11/20 Javascript
nodejs中使用worker_threads来创建新的线程的方法
2021/01/22 NodeJs
Node使用koa2实现一个简单JWT鉴权的方法
2021/01/26 Javascript
[02:58]魔廷新尊——痛苦女王至宝语音台词节选
2020/06/14 DOTA
Python中实现远程调用(RPC、RMI)简单例子
2014/04/28 Python
python中的函数用法入门教程
2014/09/02 Python
python中类的一些方法分析
2014/09/25 Python
python在新的图片窗口显示图片(图像)的方法
2019/07/11 Python
详解css position 5种不同的值的用法
2019/07/30 HTML / CSS
css3利用transform变形结合事件完成扇形导航
2020/10/26 HTML / CSS
CSS3 实现弹跳的小球动画
2020/10/26 HTML / CSS
使用phonegap播放音频的实现方法
2017/03/31 HTML / CSS
英国著名的茶叶品牌:Whittard of Chelsea
2016/09/22 全球购物
受外贸欢迎的美国主机:BlueHost
2017/05/16 全球购物
J.Crew官网:美国知名休闲服装品牌
2017/05/19 全球购物
军训自我鉴定怎么写
2014/02/13 职场文书
小学数学教研活动总结
2014/07/01 职场文书
python异常中else的实例用法
2021/06/15 Python
新手初学Java网络编程
2021/07/07 Java/Android