从组件封装看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 相关文章推荐
JavaScript控制网页层收起和展开效果的方法
Apr 15 Javascript
js闭包实现按秒计数
Apr 23 Javascript
js数组常见操作及数组与字符串相互转化实例详解
Nov 10 Javascript
JS简单实现DIV相对于浏览器固定位置不变的方法
Jun 17 Javascript
JavaScript实现三级级联特效
Nov 05 Javascript
axios发送post请求,提交图片类型表单数据方法
Mar 16 Javascript
浅析vue中常见循环遍历指令的使用 v-for
Apr 18 Javascript
jQuery实现图片下载代码
Jul 18 jQuery
Koa从零搭建到Api实现项目的搭建方法
Jul 30 Javascript
JavaScript中作用域链的概念及用途讲解
Aug 06 Javascript
如何利用JavaScript编写更好的条件语句详解
Aug 10 Javascript
Javascript中window.name属性详解
Nov 19 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 MYSQL中插入当前时间
2008/04/06 PHP
探讨GDFONTPATH能否被winxp下的php支持
2013/06/21 PHP
PHP中可以自动分割查询字符的Parse_str函数使用示例
2014/07/25 PHP
PHP传参之传值与传址的区别
2015/04/24 PHP
PHP实现导出excel数据的类库用法示例
2016/10/15 PHP
Yii全局函数用法示例
2017/01/22 PHP
php+ajax实现仿百度查询下拉内容功能示例
2017/10/20 PHP
phpstorm最新激活码分享亲测phpstorm2020.2.3版可用
2020/11/22 PHP
javascript firefox不显示本地预览图片问题的解决方法
2008/11/12 Javascript
utf-8编码引起js输出中文乱码的解决办法
2010/06/23 Javascript
js判断浏览器版本以及浏览器内核的方法
2015/01/20 Javascript
jquery validate.js表单验证入门实例(附源码)
2015/11/10 Javascript
Nodejs Stream 数据流使用手册
2016/04/17 NodeJs
浅谈JavaScript 数据属性和访问器属性
2016/09/01 Javascript
浅谈js基础数据类型和引用类型,深浅拷贝问题,以及内存分配问题
2017/09/02 Javascript
jquery 一键复制到剪切板的实例
2017/09/20 jQuery
原生javascript AJAX 三级联动的实现代码
2018/05/04 Javascript
JS中数组与对象的遍历方法实例小结
2018/08/14 Javascript
react+ant design实现Table的增、删、改的示例代码
2018/12/27 Javascript
详解Vue中CSS样式穿透问题
2019/09/12 Javascript
微信小程序抽奖组件的使用步骤
2021/01/11 Javascript
Python过滤函数filter()使用自定义函数过滤序列实例
2014/08/26 Python
python获取指定路径下所有指定后缀文件的方法
2015/05/26 Python
Python使用openpyxl读写excel文件的方法
2017/06/30 Python
Python中的单行、多行、中文注释方法
2018/07/19 Python
python语音识别实践之百度语音API
2018/08/30 Python
Python数据存储之 h5py详解
2019/12/26 Python
tensorflow入门:TFRecordDataset变长数据的batch读取详解
2020/01/20 Python
如何用python批量调整视频声音
2020/12/22 Python
python基于Kivy写一个图形桌面时钟程序
2021/01/28 Python
python实现控制台输出颜色
2021/03/02 Python
影视制作岗位职责
2013/12/04 职场文书
三年大学生活自我鉴定
2014/01/21 职场文书
《影子》教学反思
2014/02/21 职场文书
新春寄语大全
2014/04/09 职场文书
一年级学生期末评语
2014/04/21 职场文书