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


Posted in Javascript onDecember 14, 2016

问题描述

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

解决方案

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

我们的页面结构是这样的

<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组件里设定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的变化的!!!组件生命周期函数含有一个自己的参数。

componentWillReceiveProps(nextProps) {
 // 在compoWillReceiveProps里接收到Main组件里所触发onScroll事件的改变activebtn样式的index
 // 并且设置为本组件的state
 this.setState({
 activebtn: nextProps.scrollFlag.scrollIndex,
 });
}

我们的state控制我们高亮的按钮是第几个,它是一个数字。

更改导航条的样式

在这里,我使用了React周边的库:classnames,详情参见其api。

<span
 className={classnames({
 [style.informationactive]: (this.state.activebtn === 1),
 })}
 onClick={() => this.handleClick(1, 'basicinformation')}
>

在此,我们完成了一次从顶层组件触发事件,并做到函数节流,将事件一层层传递至底层展示型组件的一个过程。
最近一些关于前端开发的感慨

  1. 不要在组件中反复调用一个函数,这样会造成巨大的消耗!我们可以通过三元运算符、模板字符串做到的事情,请勿写一个新的函数。
  2. jsx不要太过于冗余。我们尽量写成变量的形式,不然页面结构复杂,不易于我们捕捉bug。
  3. 减少后端请求,能存cookie则存cookie,能存localStorge则存localStorge。
  4. 简单的组件尽量自己写,请勿使用别人的组件,否则在需求更改、样式调整上会出现巨大困难并做一些无意义的事儿。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Javascript 相关文章推荐
Javascript的IE和Firefox兼容性汇编(zz)
Feb 02 Javascript
使用非html5实现js板连连看游戏示例代码
Sep 22 Javascript
Javascript 修改String 对象 增加去除空格功能(示例代码)
Nov 30 Javascript
jQuery里filter()函数与find()函数用法分析
Jun 24 Javascript
JQuery给select添加/删除节点的实现代码
Apr 26 Javascript
基于vue实现多引擎搜索及关键字提示
Mar 16 Javascript
Vue.js结合Ueditor富文本编辑器的实例代码
Jul 11 Javascript
Node.js调用fs.renameSync报错(Error: EXDEV, cross-device link not permitted)
Dec 27 Javascript
详解React之key的使用和实践
Sep 29 Javascript
Vue中消息横向滚动时setInterval清不掉的问题及解决方法
Aug 23 Javascript
Node.js之删除文件夹(含递归删除)代码实例
Sep 09 Javascript
JS简单表单验证功能完整示例
Jan 26 Javascript
AngularJS指令与指令之间的交互功能示例
Dec 14 #Javascript
AngularJS指令与控制器之间的交互功能示例
Dec 14 #Javascript
网站申请不到支付宝接口、微信接口,免接口收款实现方式几种解决办法
Dec 14 #Javascript
AngularJS定时器的使用与移除操作方法【interval与timeout】
Dec 14 #Javascript
本地Bootstrap文件字体图标引入却无法显示问题的解决方法
Apr 18 #Javascript
微信小程序中使元素占满整个屏幕高度实现方法
Dec 14 #Javascript
AngularJS基于ngInfiniteScroll实现下拉滚动加载的方法
Dec 14 #Javascript
You might like
从零开始的异世界生活:第二季延期后,B站上架了第二部剧场版
2020/05/06 日漫
数据库相关问题
2006/10/09 PHP
浅析PHP原理之变量分离/引用(Variables Separation)
2013/08/09 PHP
PHP简单实现欧拉函数Euler功能示例
2017/11/06 PHP
php微信公众号开发之秒杀
2018/10/20 PHP
Ajax执行顺序流程及回调问题分析
2012/12/10 Javascript
jQuery实现动态表单验证时文本框抖动效果完整实例
2015/08/21 Javascript
向JavaScript的数组中添加元素的方法小结
2015/10/24 Javascript
jqGrid表格应用之新增与删除数据附源码下载
2015/12/02 Javascript
Angular2表单自定义验证器的实现
2016/10/19 Javascript
easy ui datagrid 从编辑框中获取值的方法
2017/02/22 Javascript
判断颜色是否合法的正则表达式(详解)
2017/05/03 Javascript
javascript+html5+css3自定义提示窗口
2017/06/21 Javascript
在原生不支持的旧环境中添加兼容的Object.keys实现方法
2017/09/11 Javascript
JS实现中文汉字按拼音排序的方法
2017/10/09 Javascript
vue.js整合vux中的上拉加载下拉刷新实例教程
2018/01/09 Javascript
Nautil 中使用双向数据绑定的实现
2019/10/02 Javascript
vue学习笔记之slot插槽用法实例分析
2020/02/29 Javascript
Vue动态加载图片在跨域时无法显示的问题及解决方法
2020/03/10 Javascript
详解vue-router的Import异步加载模块问题的解决方案
2020/05/13 Javascript
Python读写Redis数据库操作示例
2014/03/18 Python
Python实现抓取网页生成Excel文件的方法示例
2017/08/05 Python
浅谈django开发者模式中的autoreload是如何实现的
2017/08/18 Python
Python加载带有注释的Json文件实例
2018/05/23 Python
python下载的库包存放路径
2020/07/27 Python
pytorch使用horovod多gpu训练的实现
2020/09/09 Python
CSS3实现跳动的动画效果
2016/09/12 HTML / CSS
CSS3使用transition属性实现过渡效果
2018/04/18 HTML / CSS
基于HTML5+CSS3实现简单的时钟效果
2017/09/11 HTML / CSS
Geekbuying波兰:购买中国电子产品
2019/10/20 全球购物
医学院学生求职简历的自我评价
2013/10/24 职场文书
新学期教师寄语
2014/04/02 职场文书
酒店节能减排方案
2014/05/26 职场文书
艺术设计专业毕业生推荐信
2014/07/08 职场文书
擅自离岗检讨书
2014/09/12 职场文书
2014年终个人总结报告
2015/03/09 职场文书