资源参考:
1.每天阅读一个 npm 模块(4)- throttle-debounce(作者:elvinnn)
2.throttle-debounce 源码地址
3.element ui 源码调试地址
发现debounce参数在demo中
通过ref获取el-cascader这个所有挂载到VueComponent上的参数,并发现debounce
查找packages中casacader的源码发现
element ui使用的是throttle-debounce/debounce插件实现的debounce防抖技术
el-cascader使用throttle-debounce流程
第一步:引入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
/* 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);
};
注意事项:
自定义源码实现
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 适用于用户短时间内频繁执行某一相同操作的场景
- 用户拖动浏览器窗口改变窗口大小,触发 resize 事件。
- 用户移动鼠标,触发 mousemove 等事件。
- 用户在输入框内进入输入,触发 keydown | keypress | keyinput | keyup 等事件。
- 用户滚动屏幕,触发 scroll 事件。
- 用户在点击按钮后,由于 API 请求耗时未立即看到响应,可能会不断点击按钮触发 click 事件。
- input 实时搜索
throttle 和debounce效率图展示