玩转vue的slot内容分发


Posted in Javascript onSeptember 22, 2018

vue的内容分发非常适合“固定部分+动态部分”的组件的场景,固定部分可以是结构固定,也可以是逻辑固定,比如下拉loading,下拉loading只是中间内容是动态的,而拉到底部都会触发拉取更多内容的操作,因此我们可以把下拉loading做成一个有slot的插件。

单个Slot

在children这个标签里面放Dom,Vue不会理你,也就是不会显示,类似React:this.props.children。

//父
<children> 
  <span>12345</span>//这边不会显示
</children>

//子
components: {
  children: {
    template: "<button>为了明确作用范围,所以使用button标签</button>"
  }
}

你需要写成这样

children: {
  template: "<button><slot></slot>为了明确作用范围,所以使用button标签</button>" 
}

注意这边 slot 相当于一个坑,等着父组件给填上,这边 slot 代表的就是上面的 span

多个Slot

这边需要加name属性,说白了,多个Slot就不像上面单个,需要有个对应关系。

父-> slot="name1"
子-> <slot name="name1"

//父
<children> 
  <span slot="name1">12345</span>
</children>

//子
components: {
  children: {
    template: "<button>
            <slot name="name1"></slot>
            button标签
          </button>"
  }
}

这边写了一个name1,如果有多个,就插多个,比较简单。

使用场景

“下拉加载更多”的场景在移动端相对来说出现得比较多。我们知道下拉触底都要监听触底事件,触底的操作也相同(去后台拉取数据),分页算法也相同,因此我们会想到把它做成一个组件,重用这些相同的地方,让其他地方可以共用这个组件,从而减少代码量。

然而,下拉loading并不是一个可以完全重用的组件,因为列表里面的内容不同,空白页(没有内容时)的内容也可能不同,如果要做成组件,那么就要考虑到这方面的“不同”,因此我们想到利用vue的内容分发slot来做。下面是本人在开发的时候做的一个下拉loading,大家可以参考下。

组件代码:

<template>
 <div>
  <slot name="list" v-if="total > 0"></slot>
  <slot name="empty" v-else></slot>
 </div>
</template>
<script>
import Toast from 'lib/xl-toast'

import Tool from 'tool/tool'

export default {
 data() {
  return {
   page: 1,
   isLoading: false,
   busy: false,
   isFirstLoad: false
  }
 },
 props: {
  pageSize: {
   default: 10 // 每页展示多少条数据
  },
  total: {
   default: 0 // 总共多少条记录
  }
 },
 computed: {
  totalPage() {
   return Math.ceil(this.total / this.pageSize)
  }
 },
 created() {
  this.getList()
 },
 mounted() {
  this.addScrollListener()
 },
 methods: {
  addScrollListener() {
   // 添加监听滚动操作,用到函数防抖
   this.scrollFn = Tool.throttle(this.onScroll, 30, 30)
   document.addEventListener('scroll', this.scrollFn, false)
  },
  getList() {
   // 正在拉取数据或者没有数据了,则取消滚动监听
   if(this.isLoading || this.isFirstLoad && (this.page > this.totalPage)) {
    document.removeEventListener('scroll', this.scrollFn, false)
    return
   }
   this.busy = true
   this.isLoading = true
   // 通知父组件去拉取更多数据
   this.$emit("getList", this.page, () => {
    this.isFirstLoad = true
    this.isLoading = false
    this.page++
   }, () => {
    Toast.show('网络错误,请稍后重试')
    this.total = 0
    this.isLoading = false
   })
  },
  reset() {
   // 重新拉取数据
   this.page = 1
   this.total = 0
   this.isLoading = false
   this.isFirstLoad = false
   this.addScrollListener()
   this.getList()
  },
  onScroll() {
   // 到底拉取更多数据 
   if(Tool.touchBottom()) {
    this.getList()
   }
  }
 }
}
</script>

总之,遇到一些有想对比较固定的部分,包括js操作或者结构固定,又有一些动态的部分,我们应该就应该考虑到使用:组件+slot。

意向不到的slot另类用法

我在做需求的时候,做了一个组件,该组件分为上下两个部分,这两个部分耦合度很高(不然我怎么把它当成一个组件呢哈哈哈),如下图所示:

玩转vue的slot内容分发

本来C区域是一个组件,然后产品突然说,需要把这两个部分分开,把A移到C1的位置,C1移到A的位置(心里感觉到憋屈)。

这里我的第一个想法就是拆开来做成两个组件,但是问题来了,之前这两部分的耦合度很高,如果强制把它拆开成两个组件,那么这两个组件之间的交互必然会多很多。比如,C1改变了某个东西会影响到C2,那么C1需要触发事件通知父组件,父组件再调用C2的某个方法来更新状态。这种跨组件之间的通讯在组件之间频繁交互的情况下,将会是噩梦,而我这边却需要频繁的交互,所以如果把它拆分为两个组件,那么工作量和复杂度将会大大的增加。当然,你可以想到通过Event Hub的方式来实现两个组件之间的交互,但是根本问题还是没有实质性得得到解决。

那么,有什么方法可以做到不拆分成两个组件又能移动位置的方法呢,答案就是slot。以我的例子为例,把A和B作为C的内容分发,原来是这样的:

<A></A>
<B></B>
<C></C>

改为slot以后是这样的

<C>
<A slot="c1"></A>
<B slot="c2"></B>
</C>

这样就能做到不把C模块拆分,又能调整位置了,以最小的代价完成需求~~。

总结

vue的slot不仅可以用来内容分发,还可以用来做位置调整。如果在需要拆分组件来做位置调整,又不想因为拆分耦合度很高的组件,可以考虑使用slot来进行位置调整。一点愚见,希望对大家有所帮助。也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
用JQuery模仿淘宝的图片放大镜显示效果
Sep 15 Javascript
基于JQuery的模拟苹果桌面Dock效果(稳定版)
Oct 15 Javascript
关于js注册事件的常用方法
Apr 03 Javascript
jquery图片放大功能简单实现
Aug 01 Javascript
jQuery页面刷新(局部、全部)问题分析
Jan 09 Javascript
jQuery实现的左右移动焦点图效果
Jan 14 Javascript
JavaScript Math 对象常用方法总结
Apr 28 Javascript
vuejs 单文件组件.vue 文件的使用
Jul 28 Javascript
vue中使用iview自定义验证关键词输入框问题及解决方法
Mar 26 Javascript
Vue 项目中遇到的跨域问题及解决方法(后台php)
Mar 28 Javascript
浅谈vue父子组件怎么传值
Jul 21 Javascript
Angular 2使用路由自定义弹出组件toast操作示例
May 10 Javascript
vue 巧用过渡效果(小结)
Sep 22 #Javascript
vue forEach循环数组拿到自己想要的数据方法
Sep 21 #Javascript
vue2.x集成百度UEditor富文本编辑器的方法
Sep 21 #Javascript
vue中,在本地缓存中读写数据的方法
Sep 21 #Javascript
vuejs中监听窗口关闭和窗口刷新事件的方法
Sep 21 #Javascript
vue集成百度UEditor富文本编辑器使用教程
Sep 21 #Javascript
JavaScript 2018 中即将迎来的新功能
Sep 21 #Javascript
You might like
php下实现在指定目录搜索指定类型文件的函数
2008/10/03 PHP
做了CDN获取用户真实IP的函数代码(PHP与Asp设置方式)
2013/04/13 PHP
PHP使用DOMDocument类生成HTML实例(包含常见标签元素)
2014/06/25 PHP
php多线程实现方法及用法实例详解
2015/10/26 PHP
php上传图片并压缩的实现方法
2015/12/22 PHP
PHP文件上传处理案例分析
2016/10/15 PHP
javascript 动态设置已知select的option的value值的代码
2009/12/16 Javascript
前端开发部分总结[兼容性、DOM操作、跨域等](持续更新)
2010/03/04 Javascript
ff下JQuery无法监听input的keyup事件的解决方法
2013/12/12 Javascript
jQuery-ui引入后Vs2008的无智能提示问题解决方法
2014/02/10 Javascript
extJS中常用的4种Ajax异步提交方式
2014/03/07 Javascript
jquery调取json数据实现省市级联的方法
2015/01/29 Javascript
js实现基于正则表达式的轻量提示插件
2015/08/29 Javascript
JS实现可展开折叠层的鼠标拖曳效果
2015/10/09 Javascript
js判断文件格式及大小的简单实例(必看)
2016/10/11 Javascript
完美解决IE不支持Data.parse()的问题
2016/11/24 Javascript
详解React中合并单元格的正确写法
2019/01/08 Javascript
详解vue引入子组件方法
2019/02/12 Javascript
Webpack4+Babel7+ES6兼容IE8的实现
2019/04/10 Javascript
jQuery实现简易QQ聊天框
2020/02/10 jQuery
WebStorm无法正确识别Vue3组合式API的解决方案
2021/02/18 Vue.js
使用PDB简单调试Python程序简明指南
2015/04/25 Python
详解Python 数据库 (sqlite3)应用
2016/12/07 Python
Linux下python3.7.0安装教程
2018/07/30 Python
浅谈pycharm出现卡顿的解决方法
2018/12/03 Python
Python中xml和json格式相互转换操作示例
2018/12/05 Python
Python中的集合介绍
2019/01/28 Python
详解Django中CBV(Class Base Views)模型源码分析
2019/02/25 Python
解决python中import文件夹下面py文件报错问题
2020/06/01 Python
乌克兰巴士票购买网站:inBus
2021/03/12 全球购物
分厂厂长岗位职责
2013/12/29 职场文书
2014年全国法制宣传日宣传活动方案
2014/11/02 职场文书
永不妥协观后感
2015/06/10 职场文书
小学运动会开幕词
2016/03/04 职场文书
详解vue身份认证管理和租户管理
2021/05/25 Vue.js
pytorch model.cuda()花费时间很长的解决
2021/06/01 Python