详解Vue一个案例引发「内容分发slot」的最全总结


Posted in Javascript onDecember 02, 2018

今天我们继续来说说 Vue,目前一直在自学 Vue 然后也开始做一个项目实战,我一直认为在实战中去发现问题然后解决问题的学习方式是最好的,所以我在学习一些 Vue 的理论之后,就开始自己利用业余时间做了一个项目,然后通过项目中的一些案例进行总结。

今天我们来说说 Vue 中的内容分发 <slot> ,首先 Vue 实现了一套内容分发的 API,这套 API 是基于当前的 Web Components 规范草案,将 <slot> 元素作为承载内分发内容的出口,内容分发是 Vue 中一个非常重要的功能,很多第三方的框架库都使用到了

<slot> 功能,所以掌握这个技能是非常重要的。

它可以让我们更加优雅的使用组件。

我对 <slot> 的理解有三点或者说优势,当然,这个只是我个人的理解,如果你有不同理解的地方,欢迎交流讨论,这样才能碰出不一样的花火。

回到主题,我对内容分发的三点理解:

  • 可以优雅的包装原生的 HTML 标签
  • 组件标签可以嵌套,就像使用原生 HTML 标签一样
  • 让组件更加的通用和可复用

如果没有 <slot> 元素,当我们在组件的标签中使用组件标签或者组件标签中使用 HTML 原生标签,都是没有任何作用的,这个和我们以往使用和认识的 HTML 是相违背的。

下面我们就对这三点去做一个详细的阐述,先从一个张图开始。

详解Vue一个案例引发「内容分发slot」的最全总结

这个大家都见过,一个标准的 dialog 对话框,项目中也经常使用到,我们把它抽出来做成一个组件如下:

<div class="dialog-panel">
 <div class="dialog-header">
  <h3 class="title">标题</h3>
  <button class="close">x</button>
 </div>
 <div class="dialog-content">这是一个标准的 dialog 对话框</div>
 <div class="dialog-footer">
  <el-button type="primary" plain>取消</el-button>
  <el-button type="primary">确定</el-button>
 </div>
</div>

首先这个组件不够灵活,内容基本上是写死的,就拿标题来说,我们希望标题是可以变化的,让使用者可以传递标题进来,那么我们该如何去设计我们的这个组件呢?这就是我们今天要说的内容分发 <slot> 了,我们小小的修改下我们的例子。

<div class="dialog-panel">
 <slot></slot>
 <div class="dialog-content">这是一个标准的 dialog 对话框</div>
 <div class="dialog-footer">
  <el-button type="primary" plain>取消</el-button>
  <el-button type="primary">确定</el-button>
 </div>
</div>

在父组件中使用它

<dialog-panel>
 <div class="dialog-header">
  <h3 class="title">传递进来的标题</h3>
  <button class="close">x</button>
 </div>
</dialog-panel>

你会发现组件渲染之后, <slot> 元素会被替换成组件中包裹的元素,标题的内容完全由外部传递进来。

详解Vue一个案例引发「内容分发slot」的最全总结

上面我们只是嵌套了一个简单的 div 标签元素,插槽可以传入任何的元素,不管是HTML,还是组件元素。

插槽的默认内容

不仅如此,插槽还支持默认内容,当我们在外部没有传递给插槽内容时,我们可以给插槽一个默认的显示内容,如果外部有内容,默认的内容将会被外部的内容替换掉。

<div class="dialog-panel">
 <slot>
  <div class="dialog-header">
    <h3 class="title">这是默认标题</h3>
    <button class="close">x</button>
  </div>
 </slot>
 <div class="dialog-content">这是一个标准的 dialog 对话框</div>
 <div class="dialog-footer">
  <el-button type="primary" plain>取消</el-button>
  <el-button type="primary">确定</el-button>
 </div>
</div>

在父组件中使用它,不嵌套任何的内容时,我们的组件就会有个默认的渲染标题。

<dialog-panel>
  //无内容
</dialog-panel>

详解Vue一个案例引发「内容分发slot」的最全总结

如果我们在父组件中提供了内容,默认的内容就会被替换。

<dialog-panel>
  <div class="dialog-header">
    <h3 class="title">我是新传递的标题</h3>
    <button class="close">x</button>
  </div>
</dialog-panel>

详解Vue一个案例引发「内容分发slot」的最全总结

具名插槽

有些时候,我们除了标题有这么高的自由度之外,我们也想其它的内容也有这样的灵活性,让使用者也能通过父组件传递进来,Vue 中给我们提供了方法,我们一次可以使用很多插槽,然后给每一个插槽起个名字,也就是给我们的 <slot> 添加一个 name 属性。

于是我们就开始修改我们的对话框

<div class="dialog-panel">
 <slot name="header"></slot>
 <slot name="content"></slot>
 <slot name="footer"></slot>
</div>

我们在外部使用时,只需要提供相应名称,我们就可以渲染出我们需要的

<dialog-panel>
 <template slot="header">
  <div class="dialog-header">
   <h3 class="title">带名字的插槽</h3>
   <button class="close">x</button>
  </div>
 </template>
 <template slot="content">
  <div class="dialog-content">这是一个标准的 dialog 对话框</div>
 </template>
 <template slot="footer">
  <div class="dialog-footer">
   <el-button type="primary" plain>取消</el-button>
   <el-button type="primary">确定</el-button>
  </div>
 </template>
</dialog-panel>

详解Vue一个案例引发「内容分发slot」的最全总结

可以看到,我们在外部可以控制组件的全部内容只要我们需要,这给我们的组件带来了很高的灵活性。除了灵活性,Vue 中还给我提供了一种叫 作用域插槽 的用法,它让我们的组件更加的复用性。

具名插槽不仅仅只能用在 <template> 元素上,它也可以直接用在一个普通的元素上

<div slot="header" class="dialog-header">
  <h3 class="title">带名字的插槽</h3>
  <button class="close">x</button>
</div>

作用域插槽

作用域插槽在 Vue 中是一个非常重要的一个功能,它让组件更加的可复用性,但是官方文档上对作用域插槽的解释很令人蛋疼,反正我是看了几遍不是太理解,最后通过自己写了几个案例才明白原来可以这么厉害,如果你也和我一样一开始不太理解,不妨跟着我看看下面的案例或许对你的帮助很大。

首先我们实现一个星级评价组件

详解Vue一个案例引发「内容分发slot」的最全总结

<template>
<div class="rate-list">
  <span 
   v-for="(star, index) in stars" 
   :key="index" 
   @click="clickStart(index)"
  >
   <i v-bind:class="[star ? off : on]"></i>
  </span>
</div>
<template>
<script>
export default {
 name: "RateList",
 data() {
  return {
   off: "el-icon-star-off",
   on: "el-icon-star-on",
   rating: 2
  };
 },
 methods: {
  clickStart(index) {
   this.rating = index + 1;
  }
 },
 computed: {
  stars() {
   return [1, 2, 3, 4, 5].map(value => this.rating < value);
  }
 }
};
</script>

这是我们写死的一个星级评价组件,一直都用着还不错,可是突然有一天呢,产品说这个小星星用腻歪了能不能换个别的图标?我最近爱上了 :heart: 形

所以用这个表示吧。到这里,你可能也想到了我们把这个图标给抽离出来,放在外部,所以我们结合上面刚刚学到的 <slot> 元素去修改组件。

<div class="rate-list">
  <slot></slot>
</div>

在父组件中使用:

<rate-list>
 <span 
  v-for="(star, index) in stars" 
  :key="index" 
  @click="clickStart(index)"
 >
  <i v-bind:class="[star ? off : on]"></i>
 </span>
</rate-list>

完成之后呢,我们再也不怕以后更换内容的情况了,不管以后怎么换,我都可以在使用的时候直接在外部添加内容即可,但是似乎有一些问题,因为我们的代码看起来不是很优雅,而且我们把操作逻辑都放在的父组件中,这显然不太友好,最好的方式肯定是我们只需要在父组件中直接调用即可,所以作用域插槽这里就起到很大的作用了,我们来看看如果使用作用域插槽是如何保持优雅的。

<div class="rate-list">
  <span 
   v-for="(star, index) in stars" 
   :key="index" 
   @click="clickStart(index)"
  >
   <slot :star="star"></slot>
  </span>
</div>

在父组件中使用:

<div class="rate-list">
  <span 
   v-for="(star, index) in stars" 
   :key="index" 
   @click="clickStart(index)"
  >
   <slot :star="star"></slot>
  </span>
</div>

可以看到我们把操作逻辑全部都放在了组件内部,在外部我们只需要添加需要的元素即可,简单优雅高复用性。

在 Vue2.5.0+,slot-scope 不再限制在 <template> 元素上使用,而可以用在插槽内的任何元素或组件上

有些同学看到这里可能还没有很好的理解 作用域插槽 ,那好吧我就送佛送到西,咱继续看一个例子,我们创建一个列表面板组件。

<template>
 <div class="list-box">
  <h1>{{title}}</h1>
  <ul>
   <li v-for="(item, index) in list" :key="index">
    <slot :item="item"></slot>
   </li>
  </ul>
 </div>
</template>
<script>
export default {
 name: "List",
 props: {
  title: String,
  list: Array
 }
};
</script>

在父组件中使用:

<template>
 <div class="tirp-wrapper">
  <list title="酒店列表" :list="list">
   <span slot-scope="props">
    {{props.item.name}}
   </span>
  </list>
 </div>
</template>
<script>
import List from "./List";
export default {
 name: "Trip",
 components: { List },
 data() {
  return {
   list: [{
    name: "四季度假村酒店"
   },{
    name: "布宜诺斯艾利斯四季酒店"
   },{
    name: "孟买四季酒店"
   },{
    name: "新加坡四季酒店"
   }]
  };
 }
};
</script>

详解Vue一个案例引发「内容分发slot」的最全总结

我们再来给每个酒店添加一些描述标签,也能够完美的展示出来。

<template>
 <div class="tirp-wrapper">
  <list title="酒店列表" :list="list">
   <div slot-scope="props">
    <span>
    {{props.item.name}}
   </span>
   <el-tag>{{props.item.tag}}</el-tag>
   </div>
  </list>
 </div>
</template>
<script>
import List from "./List";
export default {
 name: "Trip",
 components: { List },
 data() {
  return {
   list: [{
     name: "四季度假村酒店",
     tag: "海滨风光"
    },{
     name: "布宜诺斯艾利斯四季酒店",
     tag: "轻奢度假"
    },{
     name: "孟买四季酒店",
     tag: "服务周到"
    },{
     name: "新加坡四季酒店",
     tag: "沙海绿洲"
    }]
  };
 }
};
</script>

详解Vue一个案例引发「内容分发slot」的最全总结

画风一转,下面我们把它变成一个消息通知列表,可以看到每个文章的点赞数量。

<template>
 <div class="tirp-wrapper">
  <list title="消息通知" :list="list">
   <div slot-scope="props">
    <span>
    {{props.item.title}}
   </span>
   <el-badge :value="props.item.value" :max="99">
    <el-button size="mini">
     <i class="fa fa-thumbs-o-up"></i>
    </el-button>
   </el-badge>
   </div>
  </list>
 </div>
</template>
<script>
import List from "./List";
export default {
 name: "Trip",
 components: { List },
 data() {
  return {
   list: [{
     title: "Vue一个案例引发「动画」的使用总结",
     value: 200
    },{
     title: "Vue一个案例引发的递归组件的使用",
     value: 20
    },{
     title: "Vue一个案例引发的动态组件与全局事件绑定总结",
     value: 50
    }]
  };
 }
};
</script>

详解Vue一个案例引发「内容分发slot」的最全总结

可以看到,不管我们如何的去修改数据结构也好,添加不同的内容也罢,我们都可以完美的完成,而且不用修改我们的子组件,只需要在外部调用时填充我们需要的内容即可。

有没有感受到作用于插槽的强大与灵活。

如果用一句话来描述作用域插槽的话:它可以让我们在父组件中访问子组件的数据,就像利用 props 属性让子组件访问到父组件的数据一样。

总结

插槽是一个重要且非常强大的功能,它可以让我们的组件具有非常 的灵活性与可复用性,而作用域插槽更加的强化了这些特性。

作用域插槽是一个不太好理解的地方,希望通过本篇文章能够让你解惑。

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

Javascript 相关文章推荐
jQuery Mobile 导航栏代码
Nov 01 Javascript
JavaScript Ajax Json实现上下级下拉框联动效果实例代码
Nov 23 Javascript
jquery css 设置table的奇偶行背景色示例
Jun 03 Javascript
JavaScript拆分字符串时产生空字符的解决方案
Sep 26 Javascript
JavaScript中关于for循环删除数组元素内容时出现的问题
Nov 21 Javascript
完美解决JS文件页面加载时的阻塞问题
Dec 18 Javascript
基于ajax与msmq技术的消息推送功能实现代码
Dec 26 Javascript
bootstrap导航栏、下拉菜单、表单的简单应用实例解析
Jan 06 Javascript
详解webpack运行Babel教程
Jun 13 Javascript
angular.js实现列表orderby排序的方法
Oct 02 Javascript
微信小程序实现录音功能
Nov 22 Javascript
WebWorker 封装 JavaScript 沙箱详情
Nov 02 Javascript
在移动端使用vue-router和keep-alive的方法示例
Dec 02 #Javascript
Angular6 Filter实现页面搜索的示例代码
Dec 02 #Javascript
GOJS+VUE实现流程图效果
Dec 01 #Javascript
JavaScript实现简单轮播图效果
Dec 01 #Javascript
jQuery-ui插件sortable实现自由拖动排序
Dec 01 #jQuery
jquery拖拽自动排序插件使用方法详解
Jul 20 #jQuery
vue实现移动端悬浮窗效果
Dec 01 #Javascript
You might like
php异步多线程swoole用法实例
2014/11/14 PHP
ThinkPHP5 验证器的具体使用
2018/05/31 PHP
JavaScript CSS修改学习第一章 查找位置
2010/02/19 Javascript
jQuery 表格工具集
2010/04/25 Javascript
jquery 页眉单行信息滚动显示实现思路及代码
2014/06/26 Javascript
使用nodejs、Python写的一个简易HTTP静态文件服务器
2014/07/18 NodeJs
jQuery新的事件绑定机制on()示例应用
2014/07/18 Javascript
简单了解JavaScript操作XPath的一些基本方法
2016/06/03 Javascript
js两种拼接字符串的简单方法(必看)
2016/09/02 Javascript
JavaScript构建自己的对象示例
2016/11/29 Javascript
Vue单页式应用(Hash模式下)实现微信分享的实例
2017/07/21 Javascript
关于vuejs中v-if和v-show的区别及v-show不起作用问题
2018/03/26 Javascript
Vue.use源码学习小结
2018/06/20 Javascript
js 判断当前时间是否处于某个一个时间段内
2019/09/19 Javascript
Vue.js 无限滚动列表性能优化方案
2019/12/02 Javascript
JavaScript命令模式原理与用法实例详解
2020/03/10 Javascript
Python中scatter函数参数及用法详解
2017/11/08 Python
python批量获取html内body内容的实例
2019/01/02 Python
ActiveMQ:使用Python访问ActiveMQ的方法
2019/01/30 Python
Django中使用Whoosh进行全文检索的方法
2019/03/31 Python
Python调用JavaScript代码的方法
2020/10/27 Python
Ubuntu权限不足无法创建文件夹解决方案
2020/11/14 Python
CSS3 border-radius圆角的实现方法及用法详解
2020/09/14 HTML / CSS
IE9下html5初试小刀
2010/09/21 HTML / CSS
中国酒类在线零售网站:酒仙网
2016/08/20 全球购物
世界最大的海报和艺术印刷商店:AllPosters.com
2017/02/01 全球购物
在职人员函授期间自我评价分享
2013/11/08 职场文书
国家励志奖学金个人先进事迹材料
2014/05/04 职场文书
励志演讲稿800字
2014/08/21 职场文书
志愿者爱心公益活动策划方案
2014/09/15 职场文书
乡镇干部党的群众路线教育实践活动个人对照检查材料
2014/09/24 职场文书
党员三严三实心得体会
2014/10/13 职场文书
2015年教师党员承诺书
2015/04/27 职场文书
CSS3 菱形拼图实现只旋转div 背景图片不旋转功能
2021/03/30 HTML / CSS
MyBatis 动态SQL全面详解
2021/10/05 MySQL
GoFrame框架数据校验之校验结果Error接口对象
2022/06/21 Golang