zoukankan      html  css  js  c++  java
  • 防抖和节流

    节流和防抖都是为了防止函数调用太快或者太多次。

    可视化比较防抖和节流

    其中_.debounce,_.throttle, raf-schd都提供了cancel()方法,用于取消延时回调

    1.防抖(debounce)

    防抖: 一段时间不活动之后发布更改。

    原理: 设置一个定时器和最小间隔时间,如果用户触发在时间间隔内,就清空定时器,重新计时;

              用户停止触发,且在最小时间间隔内没有再次触发,则发布更改。

    分类: 防抖函数有两种,一种是第一次触发执行回调函数;一种是第一次触发不执行回调函数。

    应用: 在某个动作(鼠标滚动,键盘输入)等结束后调用回调函数。

    示例1:(_.debounce)

    默认配置

     {leading: false, trailing: true}
    // 第一次默认不触发;最后一次默认触发

    代码如下:

    class App extends React.Component {
      constructor(props){
        super(props);
        this.handleChangeDebounce = debounce(this.handleChange, 250)
      }
      componentWillUnmount() {
        this.handleChangeDebounce.cancel();
      }
      handleChange = (points) => {
        console.log('done',points);
      }
      render() {
        const style ={300, height:300,border:'1px solid red'};
        return (
          <div 
            style={style} 
            // 如果想要访问事件的属性,只能通过取出事件属性传参的方式;
            // 因为debounce中,函数在setTimeout异步函数中调用,无法访问事件的属性
            onMouseMove={(e) => this.handleChangeDebounce({x: e.clientX, y: e.clientY})}>
              将鼠标在该区域移动
          </div>
        )
      }
    }

    模拟实现debounce源码:

    class App extends React.Component {
      constructor(props){
        super(props);
        this.handleMouseOverDebounced = this.debounce(this.handleMouseMove, 250)
      }
      // 发布改变
      handleMouseMove = (e) => {
        console.log('done',e.clientX)
      }
      // 简单模拟_.debounce
      debounce = (fn, time, {leading=false, trailing=true}={}) => {
        const self = this;
        return function(e) {
          // 为了异步函数中能够访问事件属性
          // e.persist()将合成事件从事件池移除,保留对原生事件对象的引用
          // 如果不需要访问事件属性,去掉该行代码
          e.persist(); 
          if (self.timer) {
            clearTimeout(self.timer);
          }
          self.timer = setTimeout(() => {
            // 异步函数
            fn.call(self, e)
          },time)
        }
      }
      componentWillUnmount() {
        clearTimeout(this.timer);
      }
      render() {
        const style ={300, height:300,border:'1px solid red'};
        return (
          <div onMouseMove={this.handleMouseOverDebounced} style={style}>
            请将鼠标在此处移动
          </div>
        )
      }
    }

    2.节流(throttle)

    节流:  基于时间的频率,抽样更改。

    原理:指定一个时间间隔timeSpace作为节流的频率。如果用户距离上次更改时间>=timeSpace,

            触发更新;否则不触发。

    示例:(_.throttle)

    默认配置:

    {leading: true, trailing: true}
    //  默认第一次和最后一次都发布改变

    代码如下:

    import {throttle} from 'lodash';
    
    class App extends React.Component {
      constructor(props){
        super(props);
        this.handleChangeThrottled = throttle(this.handleChange, 1000)
      }
      componentWillUnmount() {
        this.handleChangeThrottled.cancel();
      }
      handleChange = () => {
        console.log('done');
      }
      render() {
        const style ={300, height:300,border:'1px solid red'};
        return (
          <div style={style} onMouseMove={this.handleChangeThrottled}></div>
        )
      }
    }

    模拟实现throttle函数

    class App extends React.Component {
      constructor(props){
        super(props);
        this.handleChangeThrottled = this.throttle(this.handleChange, 1000)
      }
      componentWillUnmount() {
        this.handleChangeThrottled.cancel();
      }
      handleChange = () => {
        console.log('done');
      }
      throttle = (fn, timeSpace, {leading=true, trailing=true}={}) => {
        const self = this;
        let initialTime = leading ? 0 : new Date().getTime();
        return function(...args) {
          const now = new Date().getTime();
          // 距离上次触发经过的时间   
          const duration = now - initialTime;
          if (duration > timeSpace) {
            // 如果timeSpace时间内再次触发,说明不是最后一次,清空
            if (self.timer) {
              clearTimeout(self.timer);
              self.timer = null; //否则定时器一直递增
            }
            fn.call(self,...args);
            initialTime = now;
          } else if(trailing && !self.timer) {
            // 因为第一次触发的initialTime是0,则remaining为负数,立即触发
            const remaining = timeSpace - duration;
            // timer用于最后一次触发的定时器
            self.timer = setTimeout(() => {
              fn.call(self,...args)
            }, remaining)
          }
        }
      }
      componentWillUnmount() {
        clearTimeout(this.timer);
        this.timer = null
      }
      render() {
        const style ={300, height:300,border:'1px solid red'};
        return (
          <div 
            style={style} 
            onMouseMove={this.handleChangeThrottled}
          >
            请将鼠标在该区域移动,查看打印日志
          </div>
        )
      }
    }

    3. RequestAnimationFrame节流

    定义: 基于RequestAnimationFrame的抽样更改。

    原理: window.requestAnimationFrame是浏览器中排队等待执行的一种方法,可以在浏览器呈现性能最佳的事件执行。

              它接受一个函数作为参数,将该函数放入requestAnimationFrame的队列,在浏览器的下一帧触发。

              浏览器一般确保每秒60帧。         

    应用: 它适用了scroll或者resize等高频触发的事件。

              不适用于输入事件,因为输入事件和动画帧(该方法)基本以相同的速率触发。

              没有意义。输入事件可以使用throttle方法进行节流。

    示例:(raf-schd)

    import rafSchedule from 'raf-schd';
    
    class App extends React.Component {
      constructor(props){
        super(props);
        // rafSchedule本质上就是一个高阶函数
        this.handleScrollRaf = rafSchedule(this.handleChange)
      }
    componentWillUnMount() {
    this.handleScrollRaf.cancel()
    } handleChange
    = (points) => { console.log(points); } handleScroll = (e) => { // 此处无法访问e.clientX;可以访问target,type等属性 this.handleScrollRaf({x: e.target.scrollWidth, y: e.target.scrollHeight}) } render() { const style ={300, height:300,border:'1px solid red',overflow: 'auto'}; return ( <div onScroll={this.handleScroll} style={style}> <div style={{height: 3000,background: '#ff0'}}> 请在此块区域滚动滚动条 </div> </div> ) } }
  • 相关阅读:
    Ubuntu 各版本代号简介
    Ubuntu如何同步网络时间
    Ubuntu下修改DNS重启也能用的方法
    provider networks和self-service networks
    openstack 架构
    系统故障排除
    系统日志管理
    系统引导
    网络管理
    Linux下开启FTP服务
  • 原文地址:https://www.cnblogs.com/lyraLee/p/11465062.html
Copyright © 2011-2022 走看看