网络编程
位置:首页>> 网络编程>> JavaScript>> vue实现瀑布流组件滑动加载更多

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

作者:BenjaminShih  发布时间:2024-05-02 17:09:45 

标签:vue,瀑布流,加载

建议先看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>

来源:https://blog.csdn.net/sjn0503/article/details/75314641

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com