element ui 及时搜索防抖debounce研究探索

资源参考:

1.每天阅读一个 npm 模块(4)- throttle-debounce(作者:elvinnn)
2.throttle-debounce 源码地址
3.element ui 源码调试地址

发现debounce参数在demo中

通过ref获取el-cascader这个所有挂载到VueComponent上的参数,并发现debounce

element ui 及时搜索防抖debounce研究探索_第1张图片
element ui 及时搜索防抖debounce研究探索_第2张图片
element ui 及时搜索防抖debounce研究探索_第3张图片

查找packages中casacader的源码发现

element ui 及时搜索防抖debounce研究探索_第4张图片
element ui使用的是throttle-debounce/debounce插件实现的debounce防抖技术
element ui 及时搜索防抖debounce研究探索_第5张图片
element ui 及时搜索防抖debounce研究探索_第6张图片

el-cascader使用throttle-debounce流程

element ui 及时搜索防抖debounce研究探索_第7张图片

第一步:引入throttle-debounce/debounce

import debounce from 'throttle-debounce/debounce';

第二步:在props设定debounce方便父组件设定debounce的值

  debounce: {
      type: Number,
      default: 300
    },

第三步:在mounted用一个响应式的数据filterHandler接收

 this.filterHandler = debounce(this.debounce, () => {
      // const{inputValue}获取当前el-cascader输入的值
      const { inputValue } = this;
      if (!inputValue) {
        // 首次输入
        window.console.log('-----!inputValue-----');
        this.filtering = false;
        return;
      }
      const before = this.beforeFilter(inputValue);
      console.log('-----------')
      console.log(before)
      if (before && before.then) {
        window.console.log('-----before && before.then------');
        before.then(this.getSuggestions);
      } else if (before !== false) {
        window.console.log('----before !== false-------');
        this.getSuggestions();
      } else {
        window.console.log('------else-----');
        this.filtering = false;
      }
    });

第四步:在@input的时候触发handleInput方法中触发filterHandler

 <el-input
      ref="input"
      v-model="multiple ? presentText : inputValue"
      :size="realSize"
      :placeholder="placeholder"
      :readonly="readonly"
      :disabled="isDisabled"
      :validate-event="false"
      :class="{ 'is-focus': dropDownVisible }"
      @focus="handleInput"
      @blur="handleBlur"
      @input="handleInput">
 handleInput(val, event) {
      console.log(val);
      !this.dropDownVisible && this.toggleDropDownVisible(true);

      if (event && event.isComposing) return;
      if (val) {
        console.log(val);
        this.filterHandler();
      } else {
        this.filtering = false;
      }
    },

学习throttle-debounce

throttle-debounce作用:

npm 模块是 throttle-debounce,它提供了 throttle 和 debounce 两个函数:throttle 的含义是节流,debounce 的含义是防抖动,通过它们可以限制函数的执行频率,避免短时间内函数多次执行造成性能问题

什么是throttle

throttle:假如在短时间内同一事件多次触发,那么每隔一段更小的时间间隔就会执行事件响应函数,即该段时间内可能多次执行事件响应函数。

什么是debounce

debounce:将短时间内多次触发的事件合并成一次事件响应函数执行(往往是在第一次事件或者在最后一次事件触发时执行),即该段时间内仅一次真正执行事件响应函数。

源码学习

throttle.js

element ui 及时搜索防抖debounce研究探索_第8张图片

/* eslint-disable no-undefined,no-param-reassign,no-shadow */

/**
 * Throttle execution of a function. Especially useful for rate limiting
 * execution of handlers on events like resize and scroll.
 *
 * @param  {Number}    delay          A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher) are most useful.
 * @param  {Boolean}   [noTrailing]   Optional, defaults to false. If noTrailing is true, callback will only execute every `delay` milliseconds while the
 *                                    throttled-function is being called. If noTrailing is false or unspecified, callback will be executed one final time
 *                                    after the last throttled-function call. (After the throttled-function has not been called for `delay` milliseconds,
 *                                    the internal counter is reset)
 * @param  {Function}  callback       A function to be executed after delay milliseconds. The `this` context and all arguments are passed through, as-is,
 *                                    to `callback` when the throttled-function is executed.
 * @param  {Boolean}   [debounceMode] If `debounceMode` is true (at begin), schedule `clear` to execute after `delay` ms. If `debounceMode` is false (at end),
 *                                    schedule `callback` to execute after `delay` ms.
 *
 * @return {Function}  A new, throttled, function.
 */
module.exports = function ( 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.
	//在wrapper停止被调用后,此超时将确保在“throttle”和“end”中的适当时间执行“callback”`
	var timeoutID;
	//记录上次执行“callback”的时间。
	// Keep track of the last time `callback` was executed.
	var lastExec = 0;

	// `noTrailing` defaults to falsy.
	if ( typeof noTrailing !== 'boolean' ) {
		// debounceMode为callback
		debounceMode = callback;
		// 把函数传递给callback回调
		callback = noTrailing;
		// noTralling 为布尔值
		noTrailing = undefined;
	}

	// The `wrapper` function encapsulates all of the throttling / debouncing
	// functionality and when executed will limit the rate at which `callback`
	// is executed.
	function wrapper () {
		// 把wrapper作用域内的this赋值给self
		var self = this;
		// elapsed :计算距离最近一次函数执行后经过的时间
		var elapsed = Number(new Date()) - lastExec;
		// 赋值arguments
		var args = arguments;

		// Execute `callback` and update the `lastExec` timestamp.
		// exec()方法作用执行callback方法和更新lastExec计时器
		function exec () {
			lastExec = Number(new Date());
			// 用apply方法在callback函数中绑定准确的this指向
			callback.apply(self, args);
		}

		// If `debounceMode` is true (at begin) this is used to clear the flag
		// to allow future `callback` executions.
		//如果“debounceMode”为true(在开始处),则用于清除标志,debounce模式的时候
		//以允许将来执行“回调”。
		function clear () {
			timeoutID = undefined;
		}
		

		if ( debounceMode && !timeoutID ) {
			// debounce的时候立即执行
			// Since `wrapper` is being called for the first time and
			// `debounceMode` is true (at begin), execute `callback`.
			//因为“wrapper”是第一次调用

//'debounceMode'为true(开始时),执行'callback'。
			exec();
		}

		// Clear any existing timeout.
		if ( timeoutID ) {
			clearTimeout(timeoutID);
		}

		if ( debounceMode === undefined && elapsed > delay ) {
			// In throttle mode, if `delay` time has been exceeded, execute
			// `callback`.
			//如果经过的时间小于设置的时间间隔 delay,那么通过 setTimeout 设置一个计数器,让函数在 delay - elapsed 时间后执行
			exec();

		} else if ( noTrailing !== true ) {
			// In trailing throttle mode, since `delay` time has not been
			// exceeded, schedule `callback` to execute `delay` ms after most
			// recent execution.
			//
			// If `debounceMode` is true (at begin), schedule `clear` to execute
			// after `delay` ms.
			//
			// If `debounceMode` is false (at end), schedule `callback` to
			// execute after `delay` ms.
			//在尾部限制模式下,因为“delay”时间尚未

//超过,计划“callback”在最多时间后执行“delay”毫秒

//最近的处决。

//

//如果“debounceMode”为true(在开始处),则调度“clear”以执行

//在“delay”毫秒后。

//

//如果“debounceMode”为false(在末尾),则将“callback”计划为

//在“delay”毫秒后执行
			timeoutID = setTimeout(debounceMode ? clear : exec, debounceMode === undefined ? delay - elapsed : delay);
		}

	}

	// Return the wrapper function.
	return wrapper;

};

debounce.js
/* eslint-disable no-undefined */

var throttle = require('./throttle');

/**
 * Debounce execution of a function. Debouncing, unlike throttling,
 * guarantees that a function is only executed a single time, either at the
 * very beginning of a series of calls, or at the very end.
 *
 * @param  {Number}   delay         A zero-or-greater delay in milliseconds. For event callbacks, values around 100 or 250 (or even higher) are most useful.
 * @param  {Boolean}  [atBegin]     Optional, defaults to false. If atBegin is false or unspecified, callback will only be executed `delay` milliseconds
 *                                  after the last debounced-function call. If atBegin is true, callback will be executed only at the first debounced-function call.
 *                                  (After the throttled-function has not been called for `delay` milliseconds, the internal counter is reset).
 * @param  {Function} callback      A function to be executed after delay milliseconds. The `this` context and all arguments are passed through, as-is,
 *                                  to `callback` when the debounced-function is executed.
 *
 * @return {Function} A new, debounced function.
 */
module.exports = function ( delay, atBegin, callback ) {
	// 当callback 为undefined的时候,atBegin为noTrailing,callback为false ,当callback不为undefined, atBegin为noTrailing
	return callback === undefined ? throttle(delay, atBegin, false) : throttle(delay, callback, atBegin !== false);
};

注意事项:

element ui 及时搜索防抖debounce研究探索_第9张图片

自定义源码实现

throttle.js

function throttle(delay, callback) {
  let timeoutID;
  let lastExec = 0;

  function wrapper() {
    const self = this;
    const elapsed = Number(new Date()) - lastExec;
    const args = arguments;

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

    clearTimeout(timeoutID);

    if (elapsed > delay) {
      exec();
    } else {
      timeoutID = setTimeout(exec, delay - elapsed);
    }
  }

  return wrapper;
}



debounce.js

function debounce(delay, callback) {
  let timeoutID;

  function wrapper() {
    const self = this;
    const args = arguments;

    function exec() {
      callback.apply(self, args);
    }

    clearTimeout(timeoutID);

    timeoutID = setTimeout(exec, delay);
  }

  return wrapper;
}

debounce 与 throttle的区别就是去除了elapsed相关逻辑代码,也可以向throttle-debounce方式一样使用throtte方法来实现

throttle 和 debounce 使用场景举例

throttle 和 debounce 适用于用户短时间内频繁执行某一相同操作的场景

  1. 用户拖动浏览器窗口改变窗口大小,触发 resize 事件。
  2. 用户移动鼠标,触发 mousemove 等事件。
  3. 用户在输入框内进入输入,触发 keydown | keypress | keyinput | keyup 等事件。
  4. 用户滚动屏幕,触发 scroll 事件。
  5. 用户在点击按钮后,由于 API 请求耗时未立即看到响应,可能会不断点击按钮触发 click 事件。
  6. input 实时搜索

throttle 和debounce效率图展示

element ui 及时搜索防抖debounce研究探索_第10张图片

你可能感兴趣的:(vue,element,ui)