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

问题描述

常见的网站布局,顶部一个导航栏,我们假设本页面共有四个栏目:分别为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. 简单的组件尽量自己写,请勿使用别人的组件,否则在需求更改、样式调整上会出现巨大困难并做一些无意义的事儿。

总结

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

时间: 2016-12-11

浅谈JavaScript函数节流

浏览器中某些计算和处理要比其他的昂贵的多.例如,DOM操作比起非DOM交互需要更多的内存和CPU时间.连续尝试进行过多的DOM相关操作可能会导致 浏览器挂起,有时候甚至会崩溃.尤其在IE中使用onresize事件处理程序的时候容易发生,当调整浏览器大小的时候,该事件连续触发.在 onresize事件处理程序内部如果尝试进行DOM操作,其高频率的更改可能会让浏览器崩溃. 函数节流背后的基本思想是,某些代码不可以在没有间断的情况连续重复执行.第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代

JS中setTimeout的巧妙用法前端函数节流

什么是函数节流? 函数节流简单的来说就是不想让该函数在很短的时间内连续被调用,比如我们最常见的是窗口缩放的时候,经常会执行一些其他的操作函数,比如发一个ajax请求等等事情,那么这时候窗口缩放的时候,有可能连续发多个请求,这并不是我们想要的,或者是说我们常见的鼠标移入移出tab切换效果,有时候连续且移动的很快的时候,会有闪烁的效果,这时候我们就可以使用函数节流来操作.大家都知道,DOM的操作会很消耗或影响性能的,如果是说在窗口缩放的时候,为元素绑定大量的dom操作的话,会引发大量的连续计算,比如

JavaScript性能优化之函数节流(throttle)与函数去抖(debounce)

函数节流,简单地讲,就是让一个函数无法在很短的时间间隔内连续调用,只有当上一次函数执行后过了你规定的时间间隔,才能进行下一次该函数的调用. 函数节流的原理挺简单的,估计大家都想到了,那就是定时器.当我触发一个时间时,先setTimout让这个事件延迟一会再执行,如果在这个时间间隔内又触发了事件,那我们就clear掉原来的定时器,再setTimeout一个新的定时器延迟一会执行,就这样. 以下场景往往由于事件频繁被触发,因而频繁执行DOM操作.资源加载等重行为,导致UI停顿甚至浏览器崩溃. 1.

JavaScript函数节流概念与用法实例详解

本文实例讲述了JavaScript函数节流概念与用法.分享给大家供大家参考,具体如下: 最近在做网页的时候有个需求,就是浏览器窗口改变的时候需要改一些页面元素大小,于是乎很自然的想到了window的resize事件,于是乎我是这么写的 <!DOCTYPE html> <html> <head> <title>Throttle</title> </head> <body> <script type="text

JavaScript函数节流的两种写法

最近看了函数节流的相关内容,具体什么是节流我不讨论了,网上很多,这里总结下两个方法,我只认可其中一个.另一个貌似也能达到节流的效果但是感觉不是很正规. 方法一: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>函数节流</title> <style> div{ font-size:40px;

JavaScript 函数节流详解及方法总结

JavaScript 函数节流详解 浏览器一个网页的UI线程只有一个,他同时会处理界面的渲染和页面JavaScript代码的执行(简单扩展一下,浏览器或者JavaScript运行大环境并不是单线程,诸如ajax异步回调.hybrid框架内与native通信.事件队列.CSS运行线程等等都属于多线程环境,不过ES6引入了Promise类来减少了部分异步情况).因此当JavaScript代码运行计算量很大的方法时,就有可能阻塞UI线程,小则导致用户响应卡顿,严重的情况下浏览器会提示页面无响应是否强制

JavaScript函数节流和函数防抖之间的区别

一.概念解释 函数节流和函数防抖,两者都是优化高频率执行js代码的一种手段. 大家大概都知道旧款电视机的工作原理,就是一行行得扫描出色彩到屏幕上,然后组成一张张图片.由于肉眼只能分辨出一定频率的变化,当高频率的扫描,人类是感觉不出来的.反而形成一种视觉效果,就是一张图.就像高速旋转的风扇,你看不到扇叶,只看到了一个圆一样. 同理,可以类推到js代码.在一定时间内,代码执行的次数不一定要非常多.达到一定频率就足够了.因为跑得越多,带来的效果也是一样.倒不如,把js代码的执行次数控制在合理的范围.既

JS函数节流和函数防抖问题分析

问题1:如果实现了dom拖拽功能,但是在绑定拖拽事件的时候发现每当元素稍微移动一点便触发了大量的回调函数,导致浏览器直接卡死,这个时候怎么办? **问题2:**如果给一个按钮绑定了表单提交的post事件,但是用户有些时候在网络情况极差的情况下多次点击按钮造成表单重复提交,如何防止多次提交的发生? 为了应对如上场景,便出现了 函数防抖 和 函数节流 两个概念,总的来说: 这两个方法是在 时间轴上控制函数的执行次数. 函数防抖(debounce) 概念: 在事件被触发n秒后再执行回调,如果在这n秒内

JavaScript函数节流和函数去抖知识点学习

概念 节流 (throttle) 让一个函数不要执行的太频繁,减少执行过快的调用,叫节流 去抖 (debounce) 去抖就是对于一定时间段的连续的函数调用,只让其执行一次 throttle 应用场景 DOM 元素的拖拽功能实现(mousemove) 射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹) 计算鼠标移动的距离(mousemove) Canvas 模拟画板功能(mousemove 搜索联想(keyup 监听滚动事件判断是否到页面底部自动加载更多:给 scr

总结JavaScript三种数据存储方式之间的区别

sessionStorage .localStorage 和 cookie 之间的共同点: 都是保存在浏览器端,且同源的. sessionStorage .localStorage 和 cookie 之间的区别: cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递.而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存.cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下. 存

JAVASCRIPT style 中visibility和display之间的区别

当visibility被设置为"hidden"的时候,元素虽然被隐藏了,但它仍然占据它原来所在的位置. --------------------------------------------------------------- 例子: 复制代码 代码如下: <script language="JavaScript"> function toggleVisibility(me){ if (me.style.visibility=="hidde

浅谈JS函数节流防抖

在前端开发中有一部分的用户行为会频繁的触发事件执行,而对于DOM操作.资源加载等耗费性能的处理,很可能导致界面卡顿,甚至浏览器的崩溃.函数节流(throttle)和函数防抖(debounce)就是为了解决类似需求应运而生的. 函数节流(throttle) 函数节流就是预定一个函数只有在大于等于执行周期时才执行,周期内调用不执行.好像水滴攒到一定重量才会落下一样. 场景: 窗口调整(resize) 页面滚动(scroll) 抢购疯狂点击(mousedown) 实现: function thrott

微信小程序防止多次点击跳转(函数节流)

场景 在使用小程序的时候会出现这样一种情况:当网络条件差或卡顿的情况下,使用者会认为点击无效而进行多次点击,最后出现多次跳转页面的情况,就像下图(快速点击了两次): 解决办法 然后从 轻松理解JS函数节流和函数防抖 中找到了解决办法,就是函数节流(throttle):函数在一段时间内多次触发只会执行第一次,在这段时间结束前,不管触发多少次也不会执行函数. /utils/util.js: function throttle(fn, gapTime) { if (gapTime == null ||

javascript函数的节流[throttle]与防抖[debounce]

防抖和节流 窗口的resize.scroll,输入框内容校验等操作时,如果这些操作处理函数较为复杂或页面频繁重渲染等操作时,如果事件触发的频率无限制,会加重浏览器的负担,导致用户体验非常糟糕.此时我们可以采用debounce(防抖)和throttle(节流)的方式来减少触发的频率,同时又不影响实际效果. 这两个东西都是为了项目优化而出现的,官方是没有具体定义的,他们的出现主要是为了解决一些短时间内连续执行的事件带来性能上的不佳和内存的消耗巨大等问题: 像这类事件一般像 scroll keyup