溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

vue實(shí)現(xiàn)瀑布流組件滑動(dòng)加載更多

發(fā)布時(shí)間:2020-10-07 11:54:49 來(lái)源:腳本之家 閱讀:246 作者:BenjaminShih 欄目:web開發(fā)

建議先看vue瀑布流組件上拉加載更多再來(lái)食用本文,如果直接想看源碼文末就是~

文末新增組件優(yōu)化,之所以沒有刪優(yōu)化前的代碼是想讓以后自己還能看到走過(guò)的路。

上一篇講到在項(xiàng)目中使用上拉加載更多組件,但是由于實(shí)際項(xiàng)目開發(fā)中由于需求變更或者說(shuō)在webview中上拉加載有些機(jī)型在上拉時(shí)候會(huì)把webview也一起上拉導(dǎo)致上拉加載不靈敏等問(wèn)題,我們有時(shí)候也會(huì)換成滑動(dòng)到底部自動(dòng)加載的功能。

既然都是加載更多,很多代碼思想勢(shì)必相似,主要區(qū)別在于上拉和滑動(dòng)到底部這個(gè)操作上,所以,我們需要注意:

1、上拉加載是point指針touch觸摸事件,現(xiàn)在因?yàn)槭腔瑒?dòng)加載,需要添加scroll事件去監(jiān)聽然后執(zhí)行相應(yīng)回調(diào)
2、上拉加載主要計(jì)算觸摸滾動(dòng)距離,滑動(dòng)加載主要計(jì)算container底部和視窗上邊緣的距離

事件綁定改成:

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

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

事件回調(diào)改為:

 /**
 * 滾動(dòng)鉤子
 */
 scroll() {
 const viewHeight = global.innerHeight
 let parentNode
 if (this.container !== global) {
  parentNode = this.$el
 } else {
  parentNode = this.$el.parentNode
 }
 if (parentNode) {
  // 獲取Vue實(shí)例使用的根 DOM 元素相對(duì)于視口的位置
  const rect = parentNode.getBoundingClientRect()
  // this.distance 離底部多少距離開始加載
  // 如果此元素底邊距離視口頂部的距離小于視口高度加上distance之和,就加載下一頁(yè)
  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 {
 /**
  * 總頁(yè)數(shù)(由服務(wù)端返回)
  * @type {number}
  */
 count: 0,

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

 /**
  * 已加載次數(shù)
  * @type {number}
  */
 times: 0,

 /**
  * 已開始記載
  * @type {boolean}
  */
 started: false,

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

 dom: null,
 }
 },

 props: {
 /**
 * 初始化后自動(dòng)開始加載數(shù)據(jù)
 */
 autoload: {
 type: Boolean,
 default: true,
 },

 /**
 * 離組件最近的可滾動(dòng)父級(jí)元素(用于監(jiān)聽事件及獲取滾動(dòng)條位置)
 */
 container: {
 // Selector or Element
 default: () => (global),
 },

 /**
 * Axios請(qǐng)求參數(shù)配置對(duì)象
 * {@link https://github.com/mzabriskie/axios#request-config}
 */
 options: {
 type: Object,
 default: null,
 },

 /**
 * 起始頁(yè)碼
 */
 page: {
 type: Number,
 default: 1,
 },

 /**
 * 每頁(yè)加載數(shù)據(jù)條數(shù)
 */
 rows: {
 type: Number,
 default: 10,
 },

 /**
 * 數(shù)據(jù)加載請(qǐng)求地址
 */
 url: {
 type: String,
 default: '',
 },

 /**
 * 距離底部多遠(yuǎn)加載
 */
 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: {
 /**
 * 滾動(dòng)鉤子
 */
 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()
  }
 }
 },
 /**
 * 加載一組數(shù)據(jù)的方法
 */
 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')
 })
 },

 /**
 * 重置加載相關(guān)變量
 */
 reset() {
 this.count = 0
 this.times = 0
 this.started = false
 this.loading = false
 },

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

———————-我是分割線——————–

2017-09-18 組件優(yōu)化

我們?cè)趯懡M件時(shí)候,通常會(huì)大致先分為兩種,業(yè)務(wù)組件和通用組件,業(yè)務(wù)組件通和業(yè)務(wù)邏輯相關(guān),一般作為一個(gè)業(yè)務(wù)模塊的局部組件, 比如列表中的列表項(xiàng)組件;通用組件適用面廣,不會(huì)和業(yè)務(wù)有牽扯,比如彈出框組件。

所以我們開始封裝一個(gè)組件的時(shí)候,就要?jiǎng)澐謽I(yè)務(wù)邏輯,做什么,不做什么,從外部接收什么,向外部提供什么,這個(gè)邊界應(yīng)該非常清楚

但是之前的封裝的loadmore組件不太符合這一點(diǎn),可能是項(xiàng)目一開始比較關(guān)注功能的實(shí)現(xiàn),將其當(dāng)成的一個(gè)業(yè)務(wù)組件撰寫,現(xiàn)在有一點(diǎn)需要優(yōu)化:

之前我們傳入了各種請(qǐng)求相關(guān)的參數(shù),包括url在組件內(nèi)部完成加載和頁(yè)碼控制等一系列操作,顯然這不太符合組件功能職責(zé)單一化的原則, 其實(shí)組件內(nèi)部并不關(guān)心加載到第幾頁(yè)或者是需要請(qǐng)求什么后端接口,而只要父組件告訴自己是否還可以加載就可以了, 至于加載請(qǐng)求列表,子組件通知父組件去加載就OK。

最終我們得到一個(gè)和業(yè)務(wù)完全分離的通用組件,代碼如下:

<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,
 },

 /**
 * 滾動(dòng)外部容器, 選擇器字符串
 */
 container: {
 default: () => (global),
 },

 /**
 * 距離底部多遠(yuǎn)加載
 */
 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: {
 /**
 * 滾動(dòng)鉤子
 */
 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()
 }
 }
 },
 /**
 * 加載一組數(shù)據(jù)的方法
 */
 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>

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI