从组件封装看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 相关文章推荐
CSS+Jquery实现页面圆角框方法大全
Dec 24 Javascript
javascript中万恶的function实例分析
May 25 Javascript
本人自用的global.js库源码分享
Feb 28 Javascript
jQuery使用$.get()方法从服务器文件载入数据实例
Mar 25 Javascript
jquery实现仿JqueryUi可拖动的DIV实例
Jul 31 Javascript
jquery操作select元素和option的实例代码
Feb 03 Javascript
JS、jQuery中select的用法详解
Apr 21 Javascript
jQuery实现表格文本框淡入更改值后淡出效果
Sep 27 Javascript
AngularJS实用基础知识_入门必备篇(推荐)
Jul 10 Javascript
JavaScript使用FileReader实现图片上传预览效果
Mar 27 Javascript
JavaScript DOM元素常见操作详解【添加、删除、修改等】
May 09 Javascript
详解Node.JS模块 process
Aug 31 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中使用smarty生成静态文件的例子
2014/04/24 PHP
PHP常用处理静态操作类
2015/04/03 PHP
PHP实现在线阅读PDF文件的方法
2015/06/23 PHP
超详细的php用户注册页面填写信息完整实例(附源码)
2015/11/17 PHP
PHP 序列化和反序列化函数实例详解
2020/07/18 PHP
分析php://output和php://stdout的区别
2018/05/06 PHP
PHP扩展mcrypt实现的AES加密功能示例
2019/01/29 PHP
PHP+redis实现的限制抢购防止商品超发功能详解
2019/09/19 PHP
PHP加MySQL消息队列深入理解
2021/02/27 PHP
JavaScript跨浏览器获取页面中相同class节点的方法
2015/03/03 Javascript
JavaScript遍历求解数独问题的主要思路小结
2016/06/12 Javascript
javascript 使用正则test( )第一次是 true,第二次是false
2017/02/22 Javascript
Node.js v8.0.0正式发布!看看带来了哪些主要新特性
2017/06/02 Javascript
Vue项目中使用better-scroll实现一个轮播图自动播放功能
2018/12/03 Javascript
vue实现跨域的方法分析
2019/05/21 Javascript
使用Angular material主题定义自己的组件库的配色体系
2019/09/04 Javascript
JavaScript 截取字符串代码实例
2019/09/05 Javascript
详解如何使用React Hooks请求数据并渲染
2020/10/18 Javascript
[08:40]Navi Vs Newbee
2018/06/07 DOTA
从零学Python之引用和类属性的初步理解
2014/05/15 Python
Python3使用PySynth制作音乐的方法
2019/09/09 Python
Django实现前台上传并显示图片功能
2020/05/29 Python
html5 Canvas画图教程(9)—canvas中画出矩形和圆形
2013/01/09 HTML / CSS
梅西百货澳大利亚:Macy’s Australia
2017/07/26 全球购物
美国男士内衣品牌:Tommy John
2017/12/22 全球购物
Charles & Colvard官网:美国莫桑石品牌
2019/06/05 全球购物
俄罗斯在线手表和珠宝商店:AllTime
2019/09/28 全球购物
在浏览器端如何得到服务器端响应的XML数据
2012/11/24 面试题
好的演讲稿开场白
2013/12/30 职场文书
中学生校园广播稿
2014/01/16 职场文书
大学教师师德师风演讲稿
2014/08/22 职场文书
教师群众路线剖析材料
2014/09/29 职场文书
铁路安全反思材料
2014/12/24 职场文书
数学教师求职信范文
2015/03/20 职场文书
环境保护宣传标语大全!
2019/06/28 职场文书
MySQL 数据恢复的多种方法汇总
2021/06/21 MySQL