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 相关文章推荐
仅IE6/7/8中innerHTML返回值忽略英文空格的问题
Apr 07 Javascript
JavaScript中的undefined学习总结
Nov 30 Javascript
Extjs的FileUploadField文件上传出现了两个上传按钮
Apr 29 Javascript
Javascript中的几种URL编码方法比较
Jan 23 Javascript
AngularJS实现全选反选功能
Dec 08 Javascript
属于你的jQuery提示框(Tip)插件
Jan 20 Javascript
Bootstrap基本样式学习笔记之标签(5)
Dec 07 Javascript
jQuery动态生成不规则表格(前后端)
Feb 21 Javascript
js 监控iframe URL的变化实例代码
Jul 12 Javascript
基于Vue实现拖拽功能
Jul 29 Javascript
vue-cli初始化项目中使用less的方法
Aug 09 Javascript
Postman参数化实现过程及原理解析
Aug 13 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
DIY一个适配电脑声卡的动圈话筒放大器
2021/03/02 无线电
第八节 访问方式 [8]
2006/10/09 PHP
两个开源的Php输出Excel文件类
2010/02/08 PHP
PHP PDO函数库详解
2010/04/27 PHP
php简单压缩css样式示例
2016/09/22 PHP
thinkphp分页集成实例
2017/07/24 PHP
PHP如何防止XSS攻击与XSS攻击原理的讲解
2019/03/22 PHP
jquery select下拉框操作的一些说明
2010/04/02 Javascript
JavaScript 判断用户输入的邮箱及手机格式是否正确
2013/12/08 Javascript
PHPMyAdmin导入时提示文件大小超出PHP限制的解决方法
2015/03/30 Javascript
jquery获取多个checkbox的值异步提交给php
2015/07/07 Javascript
JavaScript设计模式开发中组合模式的使用教程
2016/05/18 Javascript
echarts3 使用总结(绘制各种图表,地图)
2017/01/05 Javascript
基于JavaScript实现窗口拖动效果
2017/01/18 Javascript
vue父组件通过props如何向子组件传递方法详解
2017/08/16 Javascript
浅谈angular4实际项目搭建总结
2017/12/01 Javascript
js实现延迟加载的几种方法详解
2019/01/19 Javascript
jQuery实现当拉动滚动条到底部加载数据的方法分析
2019/01/24 jQuery
vue-cli和v-charts实现可视化图表过程解析
2019/10/08 Javascript
Vue实现简单的拖拽效果
2020/08/25 Javascript
vue 插槽简介及使用示例
2020/11/19 Vue.js
python安装与使用redis的方法
2016/04/19 Python
python实现BP神经网络回归预测模型
2019/08/09 Python
python使用正则表达式(Regular Expression)方法超详细
2019/12/30 Python
Python运行DLL文件的方法
2020/01/17 Python
Pycharm远程连接服务器并实现代码同步上传更新功能
2020/02/25 Python
python3实现飞机大战
2020/11/29 Python
绝对令人的惊叹的CSS3折叠效果(3D效果)整理
2012/12/30 HTML / CSS
PHP面试题集
2016/12/18 面试题
个人对照检查材料思想汇报
2014/09/26 职场文书
自我查摆剖析材料
2014/10/11 职场文书
现实表现证明材料
2015/06/19 职场文书
改进工作作风心得体会
2016/01/23 职场文书
同学联谊会邀请函
2019/06/24 职场文书
Android开发手册TextInputLayout样式使用示例
2022/06/10 Java/Android
教你win10系统中APPCRASH事件问题解决方法
2022/07/15 数码科技