vue實現瀑布流元件滑動載入更多
建議先看vue瀑布流元件上拉載入更多再來食用本文,如果直接想看原始碼文末就是~
文末新增元件優化,之所以沒有刪優化前的程式碼是想讓以後自己還能看到走過的路。
上一篇講到在專案中使用上拉載入更多元件,但是由於實際專案開發中由於需求變更或者說在webview中上拉載入有些機型在上拉時候會把webview也一起上拉導致上拉載入不靈敏等問題,我們有時候也會換成滑動到底部自動載入的功能。
既然都是載入更多,很多程式碼思想勢必相似,主要區別在於上拉和滑動到底部這個操作上,所以,我們需要注意:
1、上拉載入是point指標touch觸控事件,現在因為是滑動載入,需要新增scroll事件去監聽然後執行相應回撥
事件繫結改成:
mounted() { ··· this.dom.addEventListener('scroll',this.scroll,false) ··· },beforeDestroy() { ··· this.dom.removeEventListener('scroll',
事件回撥改為:
/** * 滾動鉤子 */ 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',false) if (this.autoload && !this.loading) { this.load() } },// eslint-disable-next-line beforeDestroy() { if (this.dom) { this.dom.removeEventListener('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),data() { return { dom: null,// 外部容器dom } },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',} </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>
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。