JavaScript觸發(fā)onScroll事件的函數(shù)節(jié)流詳解
問(wèn)題描述
常見(jiàn)的網(wǎng)站布局,頂部一個(gè)導(dǎo)航欄,我們假設(shè)本頁(yè)面共有四個(gè)欄目:分別為A、B、C、D,我們點(diǎn)擊A,錨點(diǎn)跳轉(zhuǎn)至A欄目,同時(shí)頂部的A按鈕高亮;點(diǎn)擊B,錨點(diǎn)跳轉(zhuǎn)至B欄目,同時(shí)頂部的B按鈕高亮;我們?cè)贛ain組件里面滾動(dòng),滾動(dòng)到B模塊時(shí),B按鈕高亮。以上是我們經(jīng)常會(huì)在開(kāi)發(fā)中遇到的一個(gè)模型。如果是在以前,用jQuery作前端開(kāi)發(fā)的話,實(shí)在是太熟悉不過(guò)了。
解決方案
主要想談?wù)勗赗eact組件化開(kāi)發(fā)中的性能優(yōu)化方法。
我們的頁(yè)面結(jié)構(gòu)是這樣的
<div
className={style.main}
id="main"
ref={(main) => { this.main = main; }}
onScroll={
((/detail/.test(this.props.location.pathname))) ? (() => this.throttle()()) : null
}
>
{this.props.children}
<Footer />
我們?cè)趍ain組件里設(shè)定onScroll事件,在這個(gè)事件中,我們觸發(fā)action,通過(guò)redux將狀態(tài)的變化傳遞到子組件。
我的scroll事件觸發(fā)函數(shù)是這樣的(忽略一長(zhǎng)串的if else,這是一個(gè)解決了一下午的bug的終極解決方案,此文不做累述)
handleScroll() {
const { changeScrollFlag } = this.props.actions;
// 根據(jù)滾動(dòng)距離修改TitleBox的樣式
const { basicinformation, holderinformation, mainpeople, changerecord } = {
basicinformation: document.getElementById('basicinformation').offsetTop - 121,
holderinformation: document.getElementById('holderinformation').offsetTop - 121,
mainpeople: document.getElementById('mainpeople').offsetTop - 121,
changerecord: document.getElementById('changerecord').offsetTop - 121,
};
if (window.screen.availHeight > this.main.scrollTop) {
document.getElementById('gototop').style.display = 'none';
} else {
document.getElementById('gototop').style.display = 'block';
}
// 得到基礎(chǔ)信息區(qū)域、股東信息區(qū)域、主要人員區(qū)域、變更記錄區(qū)域的offsetTop,我們把它用來(lái)跟main的scrollTop比較
// 比較的結(jié)果觸發(fā)action,改變TitleBox組件樣式
if (this.main.scrollTop < holderinformation) {
// 基礎(chǔ)信息區(qū)域
if (basicinformation === -121) {
// 如果基礎(chǔ)信息模塊不存在,我們什么也不做(當(dāng)然理論上基礎(chǔ)信息模塊應(yīng)該是會(huì)有的)
return;
}
changeScrollFlag(1);
return;
} else if (this.main.scrollTop < mainpeople) {
// 股東信息區(qū)域
changeScrollFlag(2);
if (holderinformation === -121) {
// 如果股東信息欄目不存在,在滾動(dòng)的時(shí)候我們不應(yīng)該強(qiáng)行把TileBox的高亮按鈕設(shè)置為holderinformation
// 因?yàn)閔oldinformation并不存在,我們跳到前一個(gè)按鈕,讓基礎(chǔ)信息按鈕高亮
changeScrollFlag(1);
return;
}
return;
} else if (this.main.scrollTop < changerecord) {
// 主要人員區(qū)域
changeScrollFlag(3);
if (mainpeople === -121) {
// 如果主要人員欄目不存在,在滾動(dòng)的時(shí)候我們不應(yīng)該強(qiáng)行把TileBox的高亮按鈕設(shè)置為mainpeople
// mainpeople并不存在,我們跳到前一個(gè)按鈕,讓基礎(chǔ)信息按鈕高亮
changeScrollFlag(2);
if (holderinformation === -121) {
// 如果主要人員欄目不存在,而且連股東信息欄目也沒(méi)有,我們跳到高亮基礎(chǔ)信息欄目
changeScrollFlag(1);
return;
}
return;
}
return;
} else if (this.main.scrollTop > changerecord) {
// 與上面同理
// 變更記錄區(qū)域
changeScrollFlag(4);
if (changerecord === -121) {
changeScrollFlag(3);
if (mainpeople === -121) {
changeScrollFlag(2);
if (holderinformation === -121) {
changeScrollFlag(1);
return;
}
return;
}
return;
}
return;
}
}
其中,changeScrollFlag()函數(shù)是我們的action處理函數(shù)。
我們的函數(shù)節(jié)流
throttle() {
// onScroll函數(shù)節(jié)流
let previous = 0;
// previous初始設(shè)置上一次調(diào)用 onScroll 函數(shù)時(shí)間點(diǎn)為 0。
let timeout;
const wait = 250;
// 250毫秒觸發(fā)一次
return () => {
const now = Date.now();
const remaining = wait - (now - previous);
if (remaining <= 0) {
if (timeout) {
window.clearTimeout(timeout);
}
previous = now;
timeout = null;
this.handleScroll();
} else if (!timeout) {
timeout = window.setTimeout(this.handleScroll, wait);
}
};
}
我們的節(jié)流函數(shù)返回一個(gè)函數(shù),設(shè)定一個(gè)時(shí)間戳,如果我們時(shí)間戳的差值較小,我們什么也不做,但我們的時(shí)間戳的差值較大,清除定時(shí)器,觸發(fā)scroll函數(shù)。這樣看起來(lái)似乎挺簡(jiǎn)單,對(duì),確實(shí)是挺簡(jiǎn)單的。
那么在子組件我們還需要怎么做呢?
接收action
二級(jí)容器型組件接收action,通過(guò)二級(jí)容器型組件傳遞props至三級(jí)展示型組件。
我們一定要在componentWillReceiveProps接收到這個(gè)props。
記住,在componentWillReceiveProps里使用this.props是并不能夠接收到props的變化的?。?!組件生命周期函數(shù)含有一個(gè)自己的參數(shù)。
componentWillReceiveProps(nextProps) {
// 在compoWillReceiveProps里接收到Main組件里所觸發(fā)onScroll事件的改變activebtn樣式的index
// 并且設(shè)置為本組件的state
this.setState({
activebtn: nextProps.scrollFlag.scrollIndex,
});
}
我們的state控制我們高亮的按鈕是第幾個(gè),它是一個(gè)數(shù)字。
更改導(dǎo)航條的樣式
在這里,我使用了React周邊的庫(kù):classnames,詳情參見(jiàn)其api。
<span
className={classnames({
[style.informationactive]: (this.state.activebtn === 1),
})}
onClick={() => this.handleClick(1, 'basicinformation')}
>
在此,我們完成了一次從頂層組件觸發(fā)事件,并做到函數(shù)節(jié)流,將事件一層層傳遞至底層展示型組件的一個(gè)過(guò)程。
最近一些關(guān)于前端開(kāi)發(fā)的感慨
- 不要在組件中反復(fù)調(diào)用一個(gè)函數(shù),這樣會(huì)造成巨大的消耗!我們可以通過(guò)三元運(yùn)算符、模板字符串做到的事情,請(qǐng)勿寫(xiě)一個(gè)新的函數(shù)。
- jsx不要太過(guò)于冗余。我們盡量寫(xiě)成變量的形式,不然頁(yè)面結(jié)構(gòu)復(fù)雜,不易于我們捕捉bug。
- 減少后端請(qǐng)求,能存cookie則存cookie,能存localStorge則存localStorge。
- 簡(jiǎn)單的組件盡量自己寫(xiě),請(qǐng)勿使用別人的組件,否則在需求更改、樣式調(diào)整上會(huì)出現(xiàn)巨大困難并做一些無(wú)意義的事兒。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流。
- 為radio類型的INPUT添加客戶端腳本(附加實(shí)現(xiàn)JS來(lái)禁用onClick事件思路代碼)
- 怎么通過(guò)onclick事件獲取js函數(shù)返回值(代碼少)
- javascript 動(dòng)態(tài)改變onclick事件觸發(fā)函數(shù)代碼
- 詳解js的事件處理函數(shù)和動(dòng)態(tài)創(chuàng)建html標(biāo)記方法
- JavaScript利用發(fā)布訂閱模式編寫(xiě)事件監(jiān)聽(tīng)函數(shù)
- ES6中javascript實(shí)現(xiàn)函數(shù)綁定及類的事件綁定功能詳解
- 深入理解Node.js 事件循環(huán)和回調(diào)函數(shù)
- 實(shí)例講解javascript注冊(cè)事件處理函數(shù)
- js動(dòng)態(tài)添加input按鈕并給按鈕增加onclick的函數(shù)事件(帶參數(shù))完整實(shí)例
相關(guān)文章
JavaScript中for..in循環(huán)陷阱介紹
for...in循環(huán)中的循環(huán)計(jì)數(shù)器是字符串,而不是數(shù)字它包含當(dāng)前屬性的名稱或當(dāng)前數(shù)組元素的索引,下面有個(gè)不錯(cuò)的示例大家可以參考下2013-11-11
使用typescript快速開(kāi)發(fā)一個(gè)cli的實(shí)現(xiàn)示例
這篇文章主要介紹了使用typescript快速開(kāi)發(fā)一個(gè)cli的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
javascript拖拽應(yīng)用實(shí)例(二)
這篇文章主要為大家詳細(xì)介紹了javascript拖拽應(yīng)用實(shí)例,拖拽條拖到底,驗(yàn)證碼顯示出來(lái),感興趣的小伙伴們可以參考一下2016-03-03
超輕量級(jí)的js時(shí)間庫(kù)miment使用解析
這篇文章主要介紹了超輕量級(jí)的js時(shí)間庫(kù)miment使用解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08
JS+Canvas實(shí)現(xiàn)動(dòng)態(tài)時(shí)鐘效果
這篇文章主要為大家詳細(xì)介紹了JS+Canvas實(shí)現(xiàn)動(dòng)態(tài)時(shí)鐘效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10
JavaScript中AOP的實(shí)現(xiàn)與應(yīng)用
這篇文章主要給大家介紹了關(guān)于JavaScript中AOP的實(shí)現(xiàn)與應(yīng)用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用JavaScript具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05

