vue实现瀑布流组件滑动加载更多


Posted in Javascript onMarch 10, 2020

建议先看vue瀑布流组件上拉加载更多再来食用本文,如果直接想看源码文末就是~

文末新增组件优化,之所以没有删优化前的代码是想让以后自己还能看到走过的路。

上一篇讲到在项目中使用上拉加载更多组件,但是由于实际项目开发中由于需求变更或者说在webview中上拉加载有些机型在上拉时候会把webview也一起上拉导致上拉加载不灵敏等问题,我们有时候也会换成滑动到底部自动加载的功能。

既然都是加载更多,很多代码思想势必相似,主要区别在于上拉和滑动到底部这个操作上,所以,我们需要注意:

1、上拉加载是point指针touch触摸事件,现在因为是滑动加载,需要添加scroll事件去监听然后执行相应回调
2、上拉加载主要计算触摸滚动距离,滑动加载主要计算container底部和视窗上边缘的距离

事件绑定改成:

mounted() {
 ···
 this.dom.addEventListener('scroll', this.scroll, false)
 ···
 },

 beforeDestroy() {
 ···
 this.dom.removeEventListener('scroll', this.scroll, false)
 ···
 },

事件回调改为:

/**
 * 滚动钩子
 */
 scroll() {
 const viewHeight = global.innerHeight
 let parentNode
 if (this.container !== global) {
  parentNode = this.$el
 } else {
  parentNode = this.$el.parentNode
 }
 if (parentNode) {
  // 获取Vue实例使用的根 DOM 元素相对于视口的位置
  const rect = parentNode.getBoundingClientRect()
  // this.distance 离底部多少距离开始加载
  // 如果此元素底边距离视口顶部的距离小于视口高度加上distance之和,就加载下一页
  if ((rect.bottom <= viewHeight + this.distance) && this.loadable && !this.loading) {
  this.load()
  }
 }
 },

源码如下:

<template>
 <div class="loadmore" ref="loadmore">
 <div class="loadmore__body">
 <slot></slot>
 </div>
 <div class="loadmore__footer">
 <span v-if="loading">
 <i class="tc-loading"></i>
 <span>正在加载</span>
 </span>
 <span v-else-if="loadable">加载更多</span>
 <span v-else>没有更多了</span>
 </div>
 </div>
</template>

<script type="text/babel">
 import axios from 'axios'

 const CancelToken = axios.CancelToken

 export default {
 data() {
 return {
 /**
  * 总页数(由服务端返回)
  * @type {number}
  */
 count: 0,

 /**
  * 是否正在拖拽中
  * @type {boolean}
  */
 dragging: false,

 /**
  * 已加载次数
  * @type {number}
  */
 times: 0,

 /**
  * 已开始记载
  * @type {boolean}
  */
 started: false,

 /**
  * 正在加载中
  * @type {boolean}
  */
 loading: false,

 dom: null,
 }
 },

 props: {
 /**
 * 初始化后自动开始加载数据
 */
 autoload: {
 type: Boolean,
 default: true,
 },

 /**
 * 离组件最近的可滚动父级元素(用于监听事件及获取滚动条位置)
 */
 container: {
 // Selector or Element
 default: () => (global),
 },

 /**
 * Axios请求参数配置对象
 * {@link https://github.com/mzabriskie/axios#request-config}
 */
 options: {
 type: Object,
 default: null,
 },

 /**
 * 起始页码
 */
 page: {
 type: Number,
 default: 1,
 },

 /**
 * 每页加载数据条数
 */
 rows: {
 type: Number,
 default: 10,
 },

 /**
 * 数据加载请求地址
 */
 url: {
 type: String,
 default: '',
 },

 /**
 * 距离底部多远加载
 */
 distance: {
 type: Number,
 default: 200,
 },
 },

 computed: {
 /**
 * 是否可以加载
 * @returns {boolean} 是与否
 */
 loadable() {
 return !this.started || (this.page + this.times) <= this.count
 },
 },

 mounted() {
 if (this.container !== global) {
 this.dom = document.querySelector(this.container)
 } else {
 this.dom = this.container
 }
 if (!this.dom) {
 return
 }
 this.dom.addEventListener('scroll', this.scroll, false)
 if (this.autoload && !this.loading) {
 this.load()
 }
 },

 // eslint-disable-next-line
 beforeDestroy() {
 if (this.dom) {
 this.dom.removeEventListener('scroll', this.scroll, false)
 }
 },

 methods: {
 /**
 * 滚动钩子
 */
 scroll() {
 const viewHeight = global.innerHeight
 let parentNode
 if (this.container !== global) {
  parentNode = this.$el
 } else {
  parentNode = this.$el.parentNode
 }
 if (parentNode) {
  const rect = parentNode.getBoundingClientRect()
  if ((rect.bottom <= viewHeight + this.distance) && this.loadable && !this.loading) {
  this.load()
  }
 }
 },
 /**
 * 加载一组数据的方法
 */
 load() {
 if (this.loading) {
  return
 }
 this.started = true
 this.loading = true

 const params = {
  currentPage: this.page + this.times,
  pageSize: this.rows,
 }
 const options = Object.assign({}, this.options, {
  url: this.url,
  cancelToken: new CancelToken((cancel) => {
  this.cancel = cancel
  }),
 })

 if (String(options.method).toUpperCase() === 'POST') {
  options.data = Object.assign({}, options.data, params)
 } else {
  options.params = Object.assign({}, options.params, params)
 }

 this.$axios.request(options).then((res) => {
  const data = res.result
  this.times += 1
  this.loading = false
  this.count = data.pageCount
  this.$emit('success', data.list)
  this.$emit('complete')
 }).catch((e) => {
  this.loading = false
  this.$emit('error', e)
  this.$emit('complete')
 })
 },

 /**
 * 重置加载相关变量
 */
 reset() {
 this.count = 0
 this.times = 0
 this.started = false
 this.loading = false
 },

 /**
 *重新开始加载
 */
 restart() {
 this.reset()
 this.load()
 },
 },
 }
</script>

———————-我是分割线——————?

2017-09-18 组件优化

我们在写组件时候,通常会大致先分为两种,业务组件和通用组件,业务组件通和业务逻辑相关,一般作为一个业务模块的局部组件, 比如列表中的列表项组件;通用组件适用面广,不会和业务有牵扯,比如弹出框组件。

所以我们开始封装一个组件的时候,就要划分业务逻辑,做什么,不做什么,从外部接收什么,向外部提供什么,这个边界应该非常清楚

但是之前的封装的loadmore组件不太符合这一点,可能是项目一开始比较关注功能的实现,将其当成的一个业务组件撰写,现在有一点需要优化:

之前我们传入了各种请求相关的参数,包括url在组件内部完成加载和页码控制等一系列操作,显然这不太符合组件功能职责单一化的原则, 其实组件内部并不关心加载到第几页或者是需要请求什么后端接口,而只要父组件告诉自己是否还可以加载就可以了, 至于加载请求列表,子组件通知父组件去加载就OK。

最终我们得到一个和业务完全分离的通用组件,代码如下:

<template>
 <div class="loadmore" ref="loadmore">
 <div class="loadmore__body">
 <slot></slot>
 </div>
 <div class="loadmore__footer">
 <span v-if="loading">
 <i class="tc-loading"></i>
 <span>正在加载</span>
 </span>
 <span v-else-if="loading">正在加载...</span>
 <span v-else>没有更多了</span>
 </div>
 </div>
</template>

<script>
export default {
 props: {
 /**
 * 是否加载
 */
 loading: {
 type: Boolean,
 default: false,
 },

 /**
 * 滚动外部容器, 选择器字符串
 */
 container: {
 default: () => (global),
 },

 /**
 * 距离底部多远加载
 */
 distance: {
 type: Number,
 default: 200,
 },
 },

 data() {
 return {
 dom: null, // 外部容器dom
 }
 },
 mounted() {
 if (this.container !== global) {
 this.dom = document.querySelector(this.container)
 } else {
 this.dom = this.container
 }
 if (!this.dom) {
 return
 }
 this.dom.addEventListener('scroll', this.scroll, false)
 if (this.autoload && !this.loading) {
 this.load()
 }
 },

 methods: {
 /**
 * 滚动钩子
 */
 scroll() {
 if (!this.loading) {
 return
 }
 const viewHeight = global.innerHeight
 let parentNode
 if (this.container !== global) {
 parentNode = this.$el
 } else {
 parentNode = this.$el.parentNode
 }
 if (parentNode) {
 const rect = parentNode.getBoundingClientRect()
 if ((rect.bottom <= viewHeight + this.distance)) {
  this.load()
 }
 }
 },
 /**
 * 加载一组数据的方法
 */
 load() {
 this.$emit('load')
 },
 },
 beforeDestroy() {
 if (this.dom) {
 this.dom.removeEventListener('scroll', this.scroll, false)
 }
 },
}
</script>
<style lang="stylus" rel="stylesheet/stylus" scoped>
.loadmore {
 user-select: none;

 &__footer {
 color: #e4e4e4;
 padding: 20px;
 text-align: center;
 }

 .tc-loading {
 ~ span {
 vertical-align: middle;
 }
 }
}
</style>

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

Javascript 相关文章推荐
xheditor与validate插件冲突的解决方案
Apr 15 Javascript
javascript替换已有元素replaceChild()使用介绍
Apr 03 Javascript
JavaScript的null和undefined区别示例介绍
Sep 15 Javascript
javascript实现的淘宝旅行通用日历组件用法实例
Aug 03 Javascript
JS实现模拟百度搜索“2012世界末日”网页地震撕裂效果代码
Oct 31 Javascript
微信小程序  网络请求API详解
Oct 25 Javascript
JavaScript中绑定事件的三种方式及去除绑定
Nov 05 Javascript
获取JavaScript异步函数的返回值
Dec 21 Javascript
JavaScript简介_动力节点Java学院整理
Jun 26 Javascript
基于jQuery Ajax实现下拉框无刷新联动
Dec 06 jQuery
jQuery实现通过方向键控制div块上下左右移动的方法【测试可用】
Apr 26 jQuery
基于aotu.js实现微信自动添加通讯录中的联系人功能
May 28 Javascript
JavaScript代理模式原理与用法实例详解
Mar 10 #Javascript
JavaScript命令模式原理与用法实例详解
Mar 10 #Javascript
JavaScript实现指定数量的并发限制的示例代码
Mar 10 #Javascript
vue中的使用token的方法示例
Mar 10 #Javascript
vue瀑布流组件实现上拉加载更多
Mar 10 #Javascript
JS如何在数组指定位置插入元素
Mar 10 #Javascript
vue实现简单瀑布流布局
May 28 #Javascript
You might like
ExtJS与PHP、MySQL实现存储的方法
2010/04/02 PHP
无刷新动态加载数据 滚动条加载适合评论等页面
2013/10/16 PHP
php查看当前Session的ID实例
2015/03/16 PHP
PHP连接MySQL数据库并以json格式输出
2018/05/21 PHP
TP(thinkPHP)框架多层控制器和多级控制器的使用示例
2018/06/13 PHP
Jquery中增加参数与Json转换代码
2009/11/20 Javascript
改变javascript函数内部this指针指向的三种方法
2010/04/23 Javascript
JQuery入门——用bind方法绑定事件处理函数应用介绍
2013/02/05 Javascript
jQuery学习笔记之jQuery动画效果
2013/09/09 Javascript
js下拉菜单语言选项简单实现
2013/09/23 Javascript
js使用循环清空某个div中的input标签值
2014/09/29 Javascript
jQuery中:lt选择器用法实例
2014/12/29 Javascript
JS实现的简洁纵向滑动菜单(滑动门)效果
2015/10/19 Javascript
javascript自动切换焦点控制效果完整实例
2016/02/02 Javascript
详解Angularjs中的依赖注入
2016/03/11 Javascript
Bootstrap安装环境配置教程分享
2016/05/27 Javascript
深入理解JavaScript中的并行处理
2016/09/22 Javascript
如何学JavaScript?前辈的经验之谈
2016/12/28 Javascript
最全正则表达式总结:验证QQ号、手机号、Email、中文、邮编、身份证、IP地址等
2017/08/16 Javascript
详解设置Webstorm 利用babel将ES6自动转码成ES5
2017/12/20 Javascript
express框架下使用session的方法
2019/07/31 Javascript
python中的函数递归和迭代原理解析
2019/11/14 Python
Python实现将蓝底照片转化为白底照片功能完整实例
2019/12/13 Python
python统计字符串中字母出现次数代码实例
2020/03/02 Python
HTML5中的Scoped属性使用实例
2014/04/23 HTML / CSS
英国和世界各地鲜花速递专家:Arena Flowers
2018/02/10 全球购物
No7 Beauty美国官网:英国国民护肤品牌
2019/10/31 全球购物
英文简历中的自我评价
2013/10/06 职场文书
计算机相关的自我评价
2014/01/15 职场文书
电脑专业个人求职信范文
2014/02/04 职场文书
护士长竞聘书
2014/03/31 职场文书
水污染治理工程专业自荐信
2014/06/21 职场文书
医院领导班子四风对照检查材料
2014/09/27 职场文书
置业顾问岗位职责
2015/02/09 职场文书
中学教师个人总结
2015/02/10 职场文书
SQLServer2019 数据库的基本使用之图形化界面操作的实现
2021/04/08 SQL Server