vue loadmore組件上拉加載更多功能示例代碼
最近在做移動端h5頁面,所以分頁什么的就不能按照傳統(tǒng)pc端的分頁器的思維去做了,這么小的屏幕去點擊也不太方便一般來講移動端都是上拉加載更多,符合正常使用習慣。
首先簡單寫一下模板部分的html代碼,,很簡單清晰的邏輯:
<template>
<div class="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>
然后就是業(yè)務部分了
在動手寫組件之前,先理清需求:
加載頁面 -> 滑到底部 -> 上拉一定距離 -> 加載第二頁 -> 繼續(xù)前面步驟 -> 沒有更多
這是一個用戶交互邏輯,而我們需要將其映射為代碼邏輯:
首屏自動加載第一頁 -> 滑動到底部&&按下時候滑動距離Y軸有一定偏移量 -> 請求后端加載第二頁 -> 根據(jù)返回字段判斷是否還有下一頁
有了代碼邏輯,主干就出來了,加載和判斷由事件來控制,而又作為一個vue組件,我們需要配合vue生命周期來掛載事件和銷毀事件
export default {
mounted() {
// 確定容器
// 容器綁定事件
},
beforeDestory() {
// 解綁事件
},
}
如果沒有解綁的話,每次你加載組件,就會綁定一次事件…
然后我們需要一些核心事件回調方法來在合適的時間加載數(shù)據(jù)渲染頁面, 回想一下,第一我們需要http獲取數(shù)據(jù)的load函數(shù),然后我們需要三個綁定事件的回調函數(shù)pointDown(), pointMove(), pointUp(),分別對應用戶按下、移動、彈起手指操作:
export default {
···
methods:{
/**
* 加載一組數(shù)據(jù)的方法
*/
load() {
// 設置options
this.$axios.request(options).then((res) => {
// 獲取數(shù)據(jù)后的處理
}).catch((e) => {
// 異常處理
})
},
/**
* 鼠標按下事件處理函數(shù)
* @param {Object} e - 事件對象
*/
pointerdown(e) {
// 獲取按下的位置
this.pageY = e.changedTouches ? e.changedTouches[0].pageY : e.pageY
},
/**
* 鼠標移動事件處理函數(shù)
* @param {Object} e - 事件對象
*/
pointermove(e) {
const container = this.$container
const pageY = e.changedTouches ? e.changedTouches[0].pageY : e.pageY
const moveY = pageY - this.pageY
// 如果已經(jīng)向下滾動到頁面最底部
if (moveY < 0 && (container.scrollTop + Math.min(
global.innerHeight,
container.clientHeight,
)) >= container.scrollHeight) {
// 阻止原生的上拉拖動會露出頁面底部空白區(qū)域的行為(主要針對iOS版微信)
e.preventDefault()
// 如果上拉距離超過50像素,則加載下一頁
if (moveY < -50) {
this.pageY = pageY
this.load()
}
}
},
/**
* 鼠標松開事件處理函數(shù)
*/
pointerup() {
// 這邊就是取消拖動狀態(tài),需要注意在拖動過程中不要再次觸發(fā)一些事件回調,否側亂套
this.dragging = false
},
},
···
}
基本上主干已經(jīng)算完工了,一些props傳入或者一些邏輯控制細節(jié)需要再額外添加,貼出整個組件的源碼:
<template>
<div class="loadmore">
<!-- <div class="loadmore__header"></div> -->
<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 {
/**
* 總頁數(shù)(由服務端返回)
* @type {number}
*/
count: 0,
/**
* 是否正在拖拽中
* @type {boolean}
*/
dragging: false,
/**
* 已加載次數(shù)
* @type {number}
*/
times: 0,
/**
* 已開始記載
* @type {boolean}
*/
started: false,
/**
* 正在加載中
* @type {boolean}
*/
loading: false,
}
},
props: {
/**
* 初始化后自動開始加載數(shù)據(jù)
*/
autoload: {
type: Boolean,
default: true,
},
/**
* 離組件最近的可滾動父級元素(用于監(jiān)聽事件及獲取滾動條位置)
*/
container: {
// Selector or Element
default: 'body',
},
/**
* 禁用組件
*/
disabled: {
type: Boolean,
default: false,
},
/**
* Axios請求參數(shù)配置對象
* {@link https://github.com/mzabriskie/axios#request-config}
*/
options: {
type: Object,
default: null,
},
/**
* 起始頁碼
*/
page: {
type: Number,
default: 1,
},
/**
* 每頁加載數(shù)據(jù)條數(shù)
*/
rows: {
type: Number,
default: 10,
},
/**
* 數(shù)據(jù)加載請求地址
*/
url: {
type: String,
default: '',
},
},
computed: {
/**
* 是否可以加載
* @returns {boolean} 是與否
*/
loadable() {
return !this.disabled && (!this.started || (this.page + this.times) <= this.count)
},
},
mounted() {
let container = this.container
if (container) {
if (typeof container === 'string') {
container = document.querySelector(container)
} else if (!container.querySelector) {
container = document.body
}
}
if (!container) {
container = document.body
}
this.$container = container
this.onPointerDown = this.pointerdown.bind(this)
this.onPointerMove = this.pointermove.bind(this)
this.onPointerUp = this.pointerup.bind(this)
if (global.PointerEvent) {
container.addEventListener('pointerdown', this.onPointerDown, false)
container.addEventListener('pointermove', this.onPointerMove, false)
container.addEventListener('pointerup', this.onPointerUp, false)
container.addEventListener('pointercancel', this.onPointerUp, false)
} else {
container.addEventListener('touchstart', this.onPointerDown, false)
container.addEventListener('touchmove', this.onPointerMove, false)
container.addEventListener('touchend', this.onPointerUp, false)
container.addEventListener('touchcancel', this.onPointerUp, false)
container.addEventListener('mousedown', this.onPointerDown, false)
container.addEventListener('mousemove', this.onPointerMove, false)
container.addEventListener('mouseup', this.onPointerUp, false)
}
if (this.autoload) {
this.load()
}
},
// eslint-disable-next-line
beforeDestroy() {
const container = this.$container
if (global.PointerEvent) {
container.removeEventListener('pointerdown', this.onPointerDown, false)
container.removeEventListener('pointermove', this.onPointerMove, false)
container.removeEventListener('pointerup', this.onPointerUp, false)
container.removeEventListener('pointercancel', this.onPointerUp, false)
} else {
container.removeEventListener('touchstart', this.onPointerDown, false)
container.removeEventListener('touchmove', this.onPointerMove, false)
container.removeEventListener('touchend', this.onPointerUp, false)
container.removeEventListener('touchcancel', this.onPointerUp, false)
container.removeEventListener('mousedown', this.onPointerDown, false)
container.removeEventListener('mousemove', this.onPointerMove, false)
container.removeEventListener('mouseup', this.onPointerUp, false)
}
if (this.loading && this.cancel) {
this.cancel()
}
},
methods: {
/**
* 加載一組數(shù)據(jù)的方法
*/
load() {
if (this.disabled || 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()
},
/**
* 鼠標按下事件處理函數(shù)
* @param {Object} e - 事件對象
*/
pointerdown(e) {
if (this.disabled || !this.loadable || this.loading) {
return
}
this.dragging = true
this.pageY = e.changedTouches ? e.changedTouches[0].pageY : e.pageY
},
/**
* 鼠標移動事件處理函數(shù)
* @param {Object} e - 事件對象
*/
pointermove(e) {
if (!this.dragging) {
return
}
const container = this.$container
const pageY = e.changedTouches ? e.changedTouches[0].pageY : e.pageY
const moveY = pageY - this.pageY
// 如果已經(jīng)向下滾動到頁面最底部
if (moveY < 0 && (container.scrollTop + Math.min(
global.innerHeight,
container.clientHeight,
)) >= container.scrollHeight) {
// 阻止原生的上拉拖動會露出頁面底部空白區(qū)域的行為(主要針對iOS版微信)
e.preventDefault()
// 如果上拉距離超過50像素,則加載下一頁
if (moveY < -50) {
this.pageY = pageY
this.load()
}
}
},
/**
* 鼠標松開事件處理函數(shù)
*/
pointerup() {
this.dragging = false
},
},
}
</script>
以上所述是小編給大家介紹的vue loadmore組件上拉加載更多功能示例代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關文章
vue 封裝導出Excel數(shù)據(jù)的公共函數(shù)的方法
本文主要介紹了vue 封裝導出Excel數(shù)據(jù)的公共函數(shù),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09
vue根據(jù)權限動態(tài)渲染按鈕、組件等的函數(shù)式組件實現(xiàn)
這篇文章主要介紹了vue根據(jù)權限動態(tài)渲染按鈕、組件等的函數(shù)式組件實現(xiàn)方式,具有很好的參考價值,希望杜大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11
vuex實現(xiàn)數(shù)據(jù)持久化的兩種方案
這兩天在做vue項目存儲個人信息的時候,遇到了頁面刷新后個人信息數(shù)據(jù)丟失的問題,在查閱資料后,我得出兩種解決數(shù)據(jù)丟失,使用數(shù)據(jù)持久化的方法,感興趣的小伙伴跟著小編一起來看看吧2023-08-08
vue 路由懶加載中給 Webpack Chunks 命名的方法
這篇文章主要介紹了在 vue 路由懶加載中給 Webpack Chunks 命名的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04

