react中的防抖和节流,大佬封装的方法用起来就是爽--我们一起读源码

该篇文章借鉴了一下博文
https://www.jianshu.com/p/d8c3bfe10754
https://zhuanlan.zhihu.com/p/88799841

如果不想用大佬的轮子,自己写一套也可以,请查看这篇博客JavaScript防抖和节流–造轮子它不香吗

大佬的轮子只要两步就可以使用

import React, { Component } from 'react';

import { throttle, debounce } from 'throttle-debounce';//1.引入throttle

export default class Children extends Component {
	constructor(props) {
    super(props);
    /**
     * 2.把经过节流处理的函数赋值给组件中的函数
     * @param {number} 2000 是等待时间
     */
     	this.onClick = throttle(2000this.onClick);
		this.state = {

		}
	}
	render() {
		return (
			<div>
				<button onClick={this.onClick}>确定</button>
			</div>
		)
	}
	onClick = (e) => {
		console.log('e', e);
	}
}

上面是react开发大佬封装的方法,使用起来超级方便,只要两步

  • 1.引入throttle
  • 2.把经过处理的函数,赋值给需要节流的函数

学习大佬源码

接下来,艾瑞宝迪,一起去看看源码是如何实现的,看完之后,我们自己也可以造轮子

上面的引入方法是react16.8版本的,低版本16.3修改了引用方法

import throttle from 'lodash/throttle'; 

this.onClick = throttle(this.onClick, 2000);

不过此功能向下兼容,老方法在新版本中也可以使用。

下面我们基于新版本源码进行分析

'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

上面两行,使用严格模式

设置导出模块的方式(这块我不太懂,猜这个意思是设置一下导出的方式,查了部分资料还没有结果,等我弄明白了回来补充)

我弄明白了,回来补充:这个源码所在的文件是index.cjs.js也就是通过babel转化过后的commonJS代码。

给模块的输出对象增加__esModule是为了将不符合 Babel 要求的 CommonJS 模块转换成符合要求的模块,这一点在require的时候体现出来。如果加载模块之后,发现加载的模块带着一个 __esModule属性,Babel 就知道这个模块肯定是它转换过的,这样 Babel 就可以放心地从加载的模块中调用 exports.default这个导出的对象,也就是 ES6 规定的默认导出对象,所以这个模块既符合 CommonJS 标准,又符合 Babel 对 ES6 模块化的需求。然而如果__esModule不存在,也没关系,Babel 在加载了一个检测不到 __esModule的模块时,它就知道这个模块虽然符合 CommonJS 标准,但可能是一个第三方的模块,Babel 没有转换过它,如果以后直接调用exports.default是会出错的,所以现在就给它补上一个default属性,就干脆让 default属性指向它自己就好了,这样以后就不会出错了。
该解释源自分析 Babel 转换 ES6 module 的原理一文,也感谢多位前辈给予的帮助

节流函数的执行,特别适用于速率限制,比如“resize”,“scroll”这样的事件执行处理程序

 /**
 * @param  {Number}    delay         
 * @param  {Boolean}   [noTrailing] 
 * @param  {Function}  callback       
 * @param  {Boolean}   [debounceMode] 
 * @return {Function}  A new, throttled, function.
 */
delay :         number类型     以毫秒为单位的0或者更大的数字, 对于事件回调,数值100250(甚至更高)最有用。

[noTrailing] :  布尔类型       []表示可选参数,默认为false,如果设置为true,回调将会先延迟‘delay’毫秒,然后
                               再调用,如果noTrailing设置为false或者未指定,回调将会在最后一次节流函数执行后,
                               执行最后一次(在“delay”毫秒内未调用节流函数,内部定时器重置)
                               
callback :      函数类型        延迟“delay”毫秒后执行的函数,this上下文和所有参数将按原样传递,执行限制函数时
                               返回“callBack”
                               
[debounceMode]: 布尔类型        如果“debounceMode”一开始是true,在延迟“delay”毫秒后安排“clear”执行,如果
                               “debounceMode”结束的时候是false,在延迟“delay”毫秒后安排“callBack”执行。
                               
返回值:          函数类型      一个新的节流函数

function throttle (delay, noTrailing, callback, debounceMode) {
  /*
   * After wrapper has stopped being called, this timeout ensures that
   * `callback` is executed at the proper times in `throttle` and `end`
   * debounce modes.
   */
   在包装器停止调用后,此定时器确保在适当的时间,节流和结束防抖模式中被执行
   
  var timeoutID;
  var cancelled = false; // 记录回调上次记录时间
  
  var lastExec = 0; // 清除现有定时器函数
  
  function clearExistingTimeout() {
    if (timeoutID) {
      clearTimeout(timeoutID);
    }
  } // 取消下一个exec函数
  
  function cancel() {
    clearExistingTimeout();
    cancelled = true;
  } // 清除退出超时,设置取消

  if (typeof noTrailing !== 'boolean') {
    debounceMode = callback;
    callback = noTrailing;
    noTrailing = undefined;
  }
  
  //包装器函数封装了所有节流/防抖功能,执行的时候会限制回调函数的执行速率
  function wrapper() {
    var self = this;
    var elapsed = Date.now() - lastExec;
    var args = arguments;

    if (cancelled) {
      return;
    } // 执行回调,更新lastExec时间戳

    function exec() {
      lastExec = Date.now();
      callback.apply(self, args);
    }

	//如果防抖模式在开始的时候设置为true,此函数用来清理标志来保证以后回调函数的执行
    function clear() {
      timeoutID = undefined;
    }

    if (debounceMode && !timeoutID) {
      //如果是防抖模式,并且首次执行,执行回调
      exec();
    }

    clearExistingTimeout();

    if (debounceMode === undefined && elapsed > delay) {
      //在节流模式中,如果超过延迟时间,执行回调函数
      exec();
    } else if (noTrailing !== true) {
      /*
	   *在后执行节流模式中,延迟时间没有超出时,安排回调在最近一次执行后执行延迟毫秒
       *如果一开始是防抖模式,在延迟’delay‘毫秒后执行’clear‘
       *如果最后防抖模式是false,在延迟’delay‘毫秒后执行回调
	   */
      timeoutID = setTimeout(debounceMode ? clear : exec, debounceMode === undefined ? delay - elapsed : delay);
    }
  }

  wrapper.cancel = cancel; // 返回包装器函数

  return wrapper;
}

/**
 *
 * @param  {Number}   delay         
 * @param  {Boolean}  [atBegin]                                   
 * @param  {Function} callback      
 * @return {Function} A new, debounced function.
 */
函数的防抖执行,防抖不同于节流,确保函数只执行一次,无论是在一系列调用的最开始或是最后
delay:   类型数字        一个零或更高延迟毫秒,对于事件回调,数值大约100250(甚至更高)时最有效

atBegin: 布尔类型       可选,默认false,如果atBegin是false或者未指定,仅在上次取消声明的函数调用
                        后的“delay”毫秒执行回调,如果atBegin是true,回调只会在第一次防抖函数执行
                        后执行(在“delay”毫秒后节流函数没有执行的话,计时器重置)
                        
callback: 函数类型      要在延迟毫秒后执行的函数。“this”上下文和所有参数在执行取消声明的函数时按原
                        样传递给“callback”。
function debounce (delay, atBegin, callback) {
  return callback === undefined ? throttle(delay, atBegin, false) : throttle(delay, callback, atBegin !== false);
}

exports.throttle = throttle;
exports.debounce = debounce;

本文是我第一次认认真真仔仔细细的,研究每一行源码,用自己造的轮子对比大佬的,要学的还有很多,文中很多解释不到位,说明有误的地方,希望各位前辈多多指点。

发布了30 篇原创文章 · 获赞 6 · 访问量 4724

猜你喜欢

转载自blog.csdn.net/EcbJS/article/details/105216933