背景
之前写过一篇《纯JS实现加载更多(VUE框架)》,它的逻辑思路比较清晰易懂,而今天看了一天公司项目的部分功能代码,发现同事们写的加载更多的功能更加的有趣,而且易于封装到一个组件当中,便于日后的重用。
思路
同样是检测滚动,然后计算可滑动高度、滑动高度、屏幕高度然后计算什么时候可以向后台请求数据,加载数据。而不同的是这次结合了vue.js中的全局API之Vue.directive(自定义指令,关于它的知识,官网上面已经写得很清晰了),创建一个自定义指令,类似于v-show这样的指令,然后直接挂到DOM那里,再写上参数,通过directive对应的钩子函数(注意,这里的全局api也有自己相对应的钩子函数哟),来为绑定了该自定义指令的DOM节点添加滑动事件。
动手吧
首先使用全局api Vue.directive自定义自己的指令,取名为‘loadMore’,然后写下钩子函数inserted()、unbind()。
其中onScroll()函数是自定义方法,作用是实现滚动加载更多的功能。
inseted()钩子函数的作用是当被绑定元素插入父节点时,调用此钩子函数,为被绑定元素绑定监听滑动scroll的时间方法,一旦被绑定元素被监听到滑动,调用onScroll()函数实现加载更多。
unbind()钩子函数的作用是当指令与元素解绑时,调用该钩子(即退出页面或者程序时调用)
程序如下所示:
import Vue from 'vue';
Vue.directive('loadMore', { inserted(ele, data) { ele.addEventListener('scroll', data.def.onScroll.bind(null, data)) console.log(data) console.log('inserted钩子函数被触发咯~~~') }, unbind(ele, data) { ele.removeEventListener('scroll', data).def.onScroll.bind(null, data) console.log('解绑时调用!!!!') }, onScroll(data, event) { let target = event.target; let loadMoreConfig = data.value; let screenHeight = target.offsetHeight; let scrollTop = target.scrollTop; let totalHeight = target.scrollHeight; if (!loadMoreConfig.loading && totalHeight > screenHeight && totalHeight - scrollTop - screenHeight < loadMoreConfig.scrollBottomOffset) { loadMoreConfig.onScrollBottom(); loadMoreConfig.loading = true; } }, });
上面这段程序写在<script></script>里面,写在export default外面。
使用自定义指令
因为上面定义了一个指令,叫loadMore, 像v-show、v-if一样挂载DOM中,然后传需要的参数就可以了,如下
<pull-refresh :next="reloadNewKnowLedgeDataList" class="knowledge-list-container" v-load-more="newPageInfo.loadMoreConfig">……
上面传的参数是newPageInfo.loadMoreConfig,在data()里面,它是这样的
data() { return { newPageInfo: { isLoadedData: false, pageNo: 1, // 第一页 pageSize: 15, // 每一次加载的最大页数 dataList: [], loadMoreConfig: { onScrollBottom: this.onScrollBottomForNewPage, //调用加载的方法 scrollBottomOffset: 20, //距离底部门槛阈值 loading: false //是否加载 } } }
看到了吗,因为之前在全局api directive里面自己定义的监听滑动后触发函数onScroll(),一旦触发它,函数就会调用this.onScrollBottomForNewPage()方法来加载数据
接着上面按逻辑执行
onScrollBottomForNewPage() { this.newPageInfo.pageNo += 1; this.queryNewKnowLedgeDataList(); }, queryNewKnowLedgeDataList(callback) { this.loading(true); this.queryKnowLedgeDataMethod('new').then((res) => { if (callback && typeof callback === 'function') { callback(); } this.newPageInfo.isLoadedData = true; this.loading(false); this.processNewKnowLedgeData(res); }, (error) => { if (callback && typeof callback === 'function') { callback(); } this.loading(false); this.newPageInfo.loadMoreConfig.loading = true; this.newPageInfo.isLoadedData = true; if (error !== null) { this.$vux.toast.show({ text: '数据加载失败', type: 'text' }); } }); }, queryKnowLedgeDataMethod(type) { let params; if (type === 'new') { params = { 'pageNo': this.newPageInfo.pageNo, 'pageSize': this.newPageInfo.pageSize, 'findType': 0 }; } else { params = { 'pageNo': this.hotPageInfo.pageNo, 'pageSize': this.hotPageInfo.pageSize, 'findType': 1 }; } return this.$http.get('rule/findRuleNewOrHotList', { 'params': params }); }, processNewKnowLedgeData(res) { if (!res) { return; } if (res.data && res.data.length > 0) { if (this.newPageInfo.pageNo === 1) { this.newPageInfo.dataList = res.data; } else { this.newPageInfo.dataList = this.newPageInfo.dataList.concat(res.data); } this.newPageInfo.loadMoreConfig.loading = false; } else { this.newPageInfo.loadMoreConfig.loading = true; if (this.newPageInfo.pageNo === 1) { this.newPageInfo.dataList = []; } else { this.$vux.toast.show({ text: '没有更多', type: 'text' }); } } },