在網(wǎng)頁中,我們會遇到很多在滾動條到底部的時候有數(shù)據(jù)正在加載的事件汪榔,那么怎樣用vue去實現(xiàn)這樣的內(nèi)容呢赦拘?本篇只給出一個雛形,結(jié)合vue的生命周期用純javascript寫的一個監(jiān)聽函數(shù)垛贤,后續(xù)操作數(shù)據(jù)庫的部分暫且不議焰坪。
1、怎樣用純js判斷滾動條是否到底部聘惦?
先了解幾個關鍵詞:
(1)滾動條到頂部的位置:scrollTop
(2)當前窗口內(nèi)容可視區(qū):windowHeight
(3)滾動條內(nèi)容的總高度:scrollHeight
觸發(fā)監(jiān)聽的函數(shù)是:
window.onscroll = function(){...}
判斷到底部的等式: scrollTop+windowHeight=scrollHeight;
2某饰、主要函數(shù)代碼
created(){
window.onscroll = function(){
//變量scrollTop是滾動條滾動時,距離頂部的距離
var scrollTop = document.documentElement.scrollTop||document.body.scrollTop;
//變量windowHeight是可視區(qū)的高度
var windowHeight = document.documentElement.clientHeight || document.body.clientHeight;
//變量scrollHeight是滾動條的總高度
var scrollHeight = document.documentElement.scrollHeight||document.body.scrollHeight;
//滾動條到底部的條件
if(scrollTop+windowHeight==scrollHeight){
//寫后臺加載數(shù)據(jù)的函數(shù)
console.log("距頂部"+scrollTop+"可視區(qū)高度"+windowHeight+"滾動條總高度"+scrollHeight);
}
}
}
函數(shù)節(jié)流和函數(shù)防抖
原文鏈接:justclear.github.io
什么是函數(shù)節(jié)流與函數(shù)防抖
舉個栗子,我們知道目前的一種說法是當 1 秒內(nèi)連續(xù)播放 24 張以上的圖片時黔漂,在人眼的視覺中就會形成一個連貫的動畫诫尽,所以在電影的播放(以前是,現(xiàn)在不知道)中基本是以每秒 24 張的速度播放的炬守,為什么不 100 張或更多是因為 24 張就可以滿足人類視覺需求的時候牧嫉,100 張就會顯得很浪費資源。再舉個栗子减途,假設電梯一次只能載一人的話酣藻,10 個人要上樓的話電梯就得走 10 次,是一種浪費資源的行為鳍置;而實際生活正顯然不是這樣的辽剧,當電梯里有人準備上樓的時候如果外面又有人按電梯的話,電梯會再次打開直到滿載位置税产,從電梯的角度來說怕轿,這時一種節(jié)約資源的行為(相對于一次只能載一個人)。
- 函數(shù)節(jié)流: 指定時間間隔內(nèi)只會執(zhí)行一次任務砖第;
- 函數(shù)防抖: 任務頻繁觸發(fā)的情況下撤卢,只有任務觸發(fā)的間隔超過指定間隔的時候,任務才會執(zhí)行梧兼。
函數(shù)節(jié)流(throttle)
這里以判斷頁面是否滾動到底部為例,普通的做法就是監(jiān)聽 window
對象的 scroll
事件智听,然后再函數(shù)體中寫入判斷是否滾動到底部的邏輯:
$(window).on('scroll', function () {
// 判斷是否滾動到底部的邏輯
let pageHeight = $('body').height(),
scrollTop = $(window).scrollTop(),
winHeight = $(window).height(),
thresold = pageHeight - scrollTop - winHeight;
if (thresold > -100 && thresold <= 20) {
console.log('end');
}
});
這樣做的一個缺點就是比較消耗性能羽杰,因為當在滾動的時候,瀏覽器會無時不刻地在計算判斷是否滾動到底部的邏輯到推,而在實際的場景中是不需要這么做的考赛,在實際場景中可能是這樣的:在滾動過程中,每隔一段時間在去計算這個判斷邏輯莉测。而函數(shù)節(jié)流所做的工作就是每隔一段時間去執(zhí)行一次原本需要無時不刻地在執(zhí)行的函數(shù)颜骤,所以在滾動事件中引入函數(shù)的節(jié)流是一個非常好的實踐:
$(window).on('scroll', throttle(function () {
// 判斷是否滾動到底部的邏輯
let pageHeight = $('body').height(),
scrollTop = $(window).scrollTop(),
winHeight = $(window).height(),
thresold = pageHeight - scrollTop - winHeight;
if (thresold > -100 && thresold <= 20) {
console.log('end');
}
}));
加上函數(shù)節(jié)流之后,當頁面再滾動的時候捣卤,每隔 300ms
才會去執(zhí)行一次判斷邏輯忍抽。
簡單來說,函數(shù)的節(jié)流就是通過閉包保存一個標記(canRun = true
)董朝,在函數(shù)的開頭判斷這個標記是否為 true
鸠项,如果為 true
的話就繼續(xù)執(zhí)行函數(shù),否則則 return 掉子姜,判斷完標記后立即把這個標記設為 false
祟绊,然后把外部傳入的函數(shù)的執(zhí)行包在一個 setTimeout
中,最后在 setTimeout
執(zhí)行完畢后再把標記設置為 true
(這里很關鍵),表示可以執(zhí)行下一次的循環(huán)了牧抽。當 setTimeout
還未執(zhí)行的時候嘉熊,canRun
這個標記始終為 false
,在開頭的判斷中被 return 掉扬舒。
function throttle(fn, interval = 300) {
let canRun = true;
return function () {
if (!canRun) return;
canRun = false;
setTimeout(() => {
fn.apply(this, arguments);
canRun = true;
}, interval);
};
}
函數(shù)防抖(debounce)
這里以用戶注冊時驗證用戶名是否被占用為例记舆,如今很多網(wǎng)站為了提高用戶體驗,不會再輸入框失去焦點的時候再去判斷用戶名是否被占用呼巴,而是在輸入的時候就在判斷這個用戶名是否已被注冊:
$('input.user-name').on('input', function () {
$.ajax({
url: `https://just.com/check`,
method: 'post',
data: {
username: $(this).val(),
},
success(data) {
if (data.isRegistered) {
$('.tips').text('該用戶名已被注冊泽腮!');
} else {
$('.tips').text('恭喜!該用戶名還未被注冊衣赶!');
}
},
error(error) {
console.log(error);
},
});
});
很明顯诊赊,這樣的做法不好的是當用戶輸入第一個字符的時候,就開始請求判斷了府瞄,不僅對服務器的壓力增大了碧磅,對用戶體驗也未必比原來的好。而理想的做法應該是這樣的遵馆,當用戶輸入第一個字符后的一段時間內(nèi)如果還有字符輸入的話鲸郊,那就暫時不去請求判斷用戶名是否被占用。在這里引入函數(shù)防抖就能很好地解決這個問題:
$('input.user-name').on('input', debounce(function () {
$.ajax({
url: `https://just.com/check`,
method: 'post',
data: {
username: $(this).val(),
},
success(data) {
if (data.isRegistered) {
$('.tips').text('該用戶名已被注冊货邓!');
} else {
$('.tips').text('恭喜秆撮!該用戶名還未被注冊!');
}
},
error(error) {
console.log(error);
},
});
}));
其實函數(shù)防抖的原理也非常地簡單换况,通過閉包保存一個標記來保存 setTimeout
返回的值职辨,每當用戶輸入的時候把前一個 setTimeout
clear 掉,然后又創(chuàng)建一個新的 setTimeout
戈二,這樣就能保證輸入字符后的 interval
間隔內(nèi)如果還有字符輸入的話舒裤,就不會執(zhí)行 fn
函數(shù)了。
function debounce(fn, interval = 300) {
let timeout = null;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, interval);
};
}
總結(jié)
其實函數(shù)節(jié)流與函數(shù)防抖的原理非常簡單觉吭,巧妙地使用 setTimeout
來存放待執(zhí)行的函數(shù)腾供,這樣可以很方便的利用 clearTimeout
在合適的時機來清除待執(zhí)行的函數(shù)。
使用函數(shù)節(jié)流與函數(shù)防抖的目的鲜滩,在開頭的栗子中應該也能看得出來伴鳖,就是為了節(jié)約計算機資源。