JavaScript触发onScroll事件的函数节流详解

常见的网站布局,顶部一个导航栏,我们假设本页面共有四个栏目:分别为A、B、C、D,我们点击A,锚点跳转至A栏目,同时顶部的A按钮高亮;点击B,锚点跳转至B栏目,同时顶部的B按钮高亮;我们在Main组件里面滚动,滚动到B模块时,B按钮高亮。以上是我们经常会在开发中遇到的一个模型。如果是在以前,用jQuery作前端开发的话,实在是太熟悉不过了。

解决方案

主要想谈谈在React组件化开发中的性能优化方法。

我们的页面结构是这样的

<div className={style.main} ref={(main) => { this.main = main; }} onScroll={ ((/detail/.test(this.props.location.pathname))) ? (() => this.throttle()()) : null } > {this.props.children} <Footer />

我们在main组件里设定onScroll事件,在这个事件中,我们触发action,通过redux将状态的变化传递到子组件。

我的scroll事件触发函数是这样的(忽略一长串的if else,这是一个解决了一下午的bug的终极解决方案,此文不做累述)

handleScroll() { const { changeScrollFlag } = this.props.actions; // 根据滚动距离修改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'; } // 得到基础信息区域、股东信息区域、主要人员区域、变更记录区域的offsetTop,我们把它用来跟main的scrollTop比较 // 比较的结果触发action,改变TitleBox组件样式 if (this.main.scrollTop < holderinformation) { // 基础信息区域 if (basicinformation === -121) { // 如果基础信息模块不存在,我们什么也不做(当然理论上基础信息模块应该是会有的) return; } changeScrollFlag(1); return; } else if (this.main.scrollTop < mainpeople) { // 股东信息区域 changeScrollFlag(2); if (holderinformation === -121) { // 如果股东信息栏目不存在,在滚动的时候我们不应该强行把TileBox的高亮按钮设置为holderinformation // 因为holdinformation并不存在,我们跳到前一个按钮,让基础信息按钮高亮 changeScrollFlag(1); return; } return; } else if (this.main.scrollTop < changerecord) { // 主要人员区域 changeScrollFlag(3); if (mainpeople === -121) { // 如果主要人员栏目不存在,在滚动的时候我们不应该强行把TileBox的高亮按钮设置为mainpeople // mainpeople并不存在,我们跳到前一个按钮,让基础信息按钮高亮 changeScrollFlag(2); if (holderinformation === -121) { // 如果主要人员栏目不存在,而且连股东信息栏目也没有,我们跳到高亮基础信息栏目 changeScrollFlag(1); return; } return; } return; } else if (this.main.scrollTop > changerecord) { // 与上面同理 // 变更记录区域 changeScrollFlag(4); if (changerecord === -121) { changeScrollFlag(3); if (mainpeople === -121) { changeScrollFlag(2); if (holderinformation === -121) { changeScrollFlag(1); return; } return; } return; } return; } }

其中,changeScrollFlag()函数是我们的action处理函数。

我们的函数节流

throttle() { // onScroll函数节流 let previous = 0; // previous初始设置上一次调用 onScroll 函数时间点为 0。 let timeout; const wait = 250; // 250毫秒触发一次 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); } }; }

我们的节流函数返回一个函数,设定一个时间戳,如果我们时间戳的差值较小,我们什么也不做,但我们的时间戳的差值较大,清除定时器,触发scroll函数。这样看起来似乎挺简单,对,确实是挺简单的。

那么在子组件我们还需要怎么做呢?

接收action

二级容器型组件接收action,通过二级容器型组件传递props至三级展示型组件。

我们一定要在componentWillReceiveProps接收到这个props。

记住,在componentWillReceiveProps里使用this.props是并不能够接收到props的变化的!!!组件生命周期函数含有一个自己的参数。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wwfdzx.html