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