手寫vue無限滾動指令的詳細過程
概述
日常的開發(fā)當(dāng)中,為了處理大量數(shù)據(jù)的情況,一般前端會采用分頁展示,可以通過分頁插件進行數(shù)據(jù)的按需分頁請求展示,另一種解決大量數(shù)據(jù)的渲染的方式就是無限滾動,在移動端比較常見,也就是我們常見的滾動到底部加載更多數(shù)據(jù),一般web端用下拉加載更多場景不是很多,但是也還是有,比如京東和淘寶的web官方,就用到了無限滾動,通過滾動到底部,然后加載更多數(shù)據(jù)。總之,無限滾動和分頁插件都是為了解決大數(shù)據(jù)展示的問題,現(xiàn)在介紹下vue中通過自定義指令實現(xiàn)無限滾動。
最終效果

實現(xiàn)原理
在開始敲代碼之前,先講一下無限滾動的原理,首選我們需要之前的,怎么才算滾動到底部,然后我們才能去執(zhí)行加載更多的函數(shù)。
關(guān)于高度計算的幾個方法
clientHeigt
- 這兩個屬性用于獲取元素塊可視區(qū)的寬高,該屬性包括內(nèi)邊距 padding,但不包括邊框 border、外邊距 margin 和垂直滾動條

scrollHeight
- 一個元素內(nèi)容高度的度量,包括由于溢出導(dǎo)致的視圖中不可見內(nèi)容,也就是一個元素寬的實際高度,包含被滾動條卷走的部分。具體看下圖:

scrollTop
- 滾動條卷走的高度。,參考下圖:

綜上
得出滾動條到達底部的計算公式為:clientHeight + scrollTop == scrollHeight,知道這個之后,我們寫邏輯就容易多了,只需要在滾動條到達底部的時候,重新取獲取數(shù)據(jù)就可以了。
目錄結(jié)構(gòu)

App.vue
無限滾動首選需要一個固定高度的盒子然后設(shè)置 style="overflow: auto" 然后可以根據(jù)需要加上滾動結(jié)束的限制,比如loading等
<template>
<div id="app">
//外層包裹盒子
<div
class="infinite-list"
v-infinite-scroll.loading.complated.immediate="load"
style="overflow: auto"
ref="infiniteList"
>
<ul>
<li v-for="i in count" class="infinite-list-item">{{ i }}</li>
</ul>
//加載中
<p v-if="loading && !complated" class="text">加載中...</p>
//結(jié)束了
<p v-if="complated" class="text">沒有更多了</p>
</div>
</div>
</div>
</template>
<script>
import Velocity from "velocity-animate";
import { DatePicker } from "./components/DatePicker/index";
export default {
name: "App",
components: {
DatePicker,
},
data() {
count: 1,
loading: false,
complated: false,
};
},
methods: {
//滾動到底部的處理邏輯
load() {
// 以下是定時器模擬異步數(shù)據(jù)請求,可根據(jù)自己的需求進行變更
this.loading = true;
setTimeout(() => {
if (this.count >= 15) {
this.complated = true;
return;
}
this.count += 3;
this.loading = false;
}, 1000);
},
},
};
</script>
<style lang="less">
#app {
.infinite-list {
height: 300px;
width: 500px;
border: 1px solid red;
li {
height: 50px;
background: #e8f3fe;
margin: 10px;
color: #7dbcfc;
text-align: center;
line-height: 50px;
}
.text {
color: green;
text-align: center;
line-height: 50px;
}
}
}
</style>./components/v-infinite-scroll/index.js
import { checkArriveBottom } from "./utils";
export default {
install(Vue) {
Vue.directive("infinite-scroll", {
// 指令在插入的時候會執(zhí)行一次
inserted: function (el, binding, vnode) {
const fn = binding.value;
const context = vnode.context;
let timer = null;
// 指令的值必須是一個函數(shù),我們好執(zhí)行回調(diào)
if (typeof fn != "function") {
throw new Error("指令value必須為函數(shù)");
}
// 事件處理函數(shù)
function handleScroll() {
// 判斷滾動條到達底部了,才開始執(zhí)行回調(diào)
if (checkArriveBottom(el)) {
// 執(zhí)行回調(diào)的時候,要把this指向組件實例
fn.bind(context)();
}
}
// 將滾動處理函數(shù)掛載到對應(yīng)組件實例上面,便于組件更新的時候,對設(shè)置了loading和complate屬性進行移除事件綁定
context.handleScroll = handleScroll;
// 如果設(shè)置有immediate說明立即執(zhí)行,則立即執(zhí)行回調(diào),直到將內(nèi)容撐滿內(nèi)容區(qū)
if (binding?.modifiers?.immediate) {
timer = setInterval(() => {
// 子元素的總高度大于設(shè)置指令的父級包裹元素就表示填滿了可視區(qū),停止加載
const childScrollHeight = el.firstElementChild.scrollHeight;
if (childScrollHeight >= el.clientHeight) {
return clearInterval(timer);
}
handleScroll();
}, 1500);
}
// 綁定滾動處理函數(shù)
el.addEventListener("scroll", context.handleScroll);
},
// 組件更新的時候,會不斷觸發(fā)(最明顯就是data中的響應(yīng)式數(shù)據(jù)變化,會繼續(xù)執(zhí)行update方法)
update(el, binding, vnode) {
const context = vnode.context;
// 如果加載中或者已經(jīng)加載完了,就移除滾動事件
if (
(binding?.modifiers?.complated && context.complated) ||
(binding?.modifiers?.loading && context.loading)
) {
el.removeEventListener("scroll", context.handleScroll);
} else {
// 當(dāng)loading和complate都是false的時候,表示可以繼續(xù)加載
el.addEventListener("scroll", context.handleScroll);
}
},
});
},
};./components/v-infinite-scroll/utils.js
/**
* @Description 用于判斷滾動條是否到達底部
* @param { Element }
* @return { Boolean }
**/
export function checkArriveBottom(el) {
const clientHeight = el.clientHeight;
const scrollTop = el.scrollTop;
const scrollHeight = el.scrollHeight;
//可以設(shè)置>=就行,這里也可以設(shè)置距離底部一定距離,自定義,不一定非要到達底部
return clientHeight + scrollTop >= scrollHeight;
}./components/v-infinite-scroll/main.js
import Vue from "vue";
import App from "./App.vue";
import vInfiniteScroll from "./components/v-infinite-scroll";
Vue.use(vInfiniteScroll);
Vue.use(myUi);
new Vue({
render: (h) => h(App),
}).$mount("#app");總結(jié)
完成上述指令,需要先閱讀官網(wǎng)自定義指令文檔,搞懂具體指令的一些鉤子函數(shù)的用途以及觸發(fā)時機,還有就是參數(shù)的意義,鏈接放這里cn.vuejs.org/v2/guide/cu…
到此這篇關(guān)于手寫vue無限滾動指令的文章就介紹到這了,更多相關(guān)vue無限滾動指令內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue將毫秒數(shù)轉(zhuǎn)化為正常日期格式的實例
今天小編就為大家分享一篇vue將毫秒數(shù)轉(zhuǎn)化為正常日期格式的實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09
Vue.js+HighCharts實現(xiàn)動態(tài)請求展示時序數(shù)據(jù)
這篇文章主要為大家詳細介紹了Vue.js+HighCharts實現(xiàn)動態(tài)請求展示時序數(shù)據(jù),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03
vue項目引入遠程jweixin-1.2.0.js文件并使用詳解
這篇文章主要介紹了vue項目引入遠程jweixin-1.2.0.js文件并使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-03-03

