从组件封装看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 相关文章推荐
用正则xmlHttp实现的偷(转)
Jan 22 Javascript
关于jquery css的使用介绍
Apr 18 Javascript
jQuery事件绑定和委托实例
Nov 25 Javascript
jquery制作LED 时钟特效
Feb 01 Javascript
JS实现的新浪微博大厅文字内容滚动效果代码
Nov 05 Javascript
深入理解MVC中的时间js格式化
May 19 Javascript
BootStrap中Datepicker控件带中文的js文件
Aug 10 Javascript
微信小程序开发之相册选择和拍照详解及实例代码
Feb 22 Javascript
Vue自定义指令使用方法详解
Aug 21 Javascript
js判断文件类型大小并给出提示的实现方法
Jan 03 Javascript
使用vue实现grid-layout功能实例代码
Jan 05 Javascript
原生js拖拽功能制作滑动条实例代码
Feb 05 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
Snoopy类使用小例子
2008/04/15 PHP
浅谈php正则表达式中的非贪婪模式匹配的使用
2014/11/25 PHP
ThinkPHP5&amp;5.1实现验证码的生成、使用及点击刷新功能示例
2020/02/07 PHP
jquery autocomplete自动完成插件的的使用方法
2010/08/07 Javascript
基于jQuery实现的百度导航li拖放排列效果,即时更新数据库
2012/07/31 Javascript
Mac地址验证的javascript代码
2013/11/09 Javascript
jquery中push()的用法(数组添加元素)
2014/11/25 Javascript
jQuery内容过滤选择器用法示例
2016/09/09 Javascript
jQuery 插件实现随机自由弹跳气泡样式
2017/01/12 Javascript
Layui table 组件的使用之初始化加载数据、数据刷新表格、传参数
2017/09/11 Javascript
浅谈JS 数字和字符串之间相互转化的纠纷
2017/10/20 Javascript
vue-cli3 项目从搭建优化到docker部署的方法
2019/01/28 Javascript
微信小程序实现购物车代码实例详解
2019/08/29 Javascript
es5 类与es6中class的区别小结
2020/11/09 Javascript
[03:56]显微镜下的DOTA2第十一期——鬼畜的死亡先知播音员
2014/06/23 DOTA
[06:14]《辉夜杯》外卡赛附加赛 4支战队巡礼
2015/10/23 DOTA
Python进阶之尾递归的用法实例
2018/01/31 Python
Python解压 rar、zip、tar文件的方法
2019/11/19 Python
pyqt5 QlistView列表显示的实现示例
2020/03/24 Python
django自定义非主键自增字段类型详解(auto increment field)
2020/03/30 Python
Python代码中如何读取键盘录入的值
2020/05/27 Python
tensorflow之读取jpg图像长和宽实例
2020/06/18 Python
python读取excel数据并且画图的实现示例
2021/02/08 Python
CSS3 animation实现简易幻灯片轮播特效
2016/09/27 HTML / CSS
英国轻奢珠宝品牌:Astley Clarke
2016/12/18 全球购物
俄罗斯苹果优质经销商商店:iPort
2020/05/27 全球购物
shell程序中如何注释
2012/01/28 面试题
小区文明倡议书
2014/05/16 职场文书
学生党员公开承诺书
2014/05/28 职场文书
运动会口号大全
2014/06/07 职场文书
大学生学习面向未来的赶考思想汇报
2014/09/12 职场文书
2014年党风建设工作总结
2014/11/19 职场文书
酒店采购员岗位职责
2015/04/03 职场文书
Win11怎么进入安全模式?Windows 11进入安全模式的方法
2021/11/21 数码科技
Redis实现分布式锁的五种方法详解
2022/06/14 Redis
Docker容器harbor私有仓库部署和管理
2022/08/05 Servers