防抖节流
-
什么是防抖节流?
防抖(Debounce):防止重复点击触发事件(最后一个人说了算,在某段时间内,不管你触发了多少次回调,我都只认最后一次)
应用场景:
- 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
- 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
- 文本编辑器实时保存,当无任何更改操作一秒后进行保存
- ...
代码实现要点:设置一个定时器,通过闭包,抓住定时器变量,控制定时器的添加和删除
//非立即执行版:触发事件后函数不会立即执行,而是在n秒后执行,如果在n秒内又触发了事件,则会重新计算函数执行时间
function debounce(fn, time) {
let timer
return function () {
const args = arguments
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, time)
}
}
// 立即执行版:触发事件后函数会立即执行,n秒内不触发事件才能继续执行函数的效果
function debounce(fn, time) {
let timer;
return function () {
const ctx = this;
const args = arguments;
let callNow = !timer;
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
}, time);
if (callNow) fn.apply(ctx, args);
};
}
//结合版本
/**
* @desc 函数防抖
* @param fn 函数
* @param wait 延迟执行毫秒数
* @param immediate true 表立即执行,false 表非立即执行
*/
function debounce (fn, wait, immediate) {
let timer
return function () {
const ctx = this
const args = arguments
if (timer) clearTimeout(timer)
// 立即执行
if (immediate) {
const callNow = !timer
timer = setTimeout(() => {
timer = null
}, wait)
if (callNow) fn.apply(ctx, args)
} else {
timer = setTimeout(() => {
fn.apply(ctx, args)
}, wait)
}
}
}
节流(Throttle):指定时间间隔内只会执行一次任务(在某段时间内,不管你触发了多少次回调,我都只认第一次,并在计时结束时给予响应)
代码实现要点:主要分定时器版和时间戳版
场景:
- scroll 事件,每隔一秒计算一次位置信息等
- 浏览器播放事件,每隔一秒计算一次进度信息等
- input 框实时搜索并发送请求展示下拉列表,没隔一秒发送一次请求 (也可做防抖)
//定时器版:函数不会立即执行,并且每x秒执行一次,在停止触发事件后,函数还会再执行一次
function throttle(fn, time) {
let timeout;
return function () {
if (!timeout) {
timeout = setTimeout(() => {
fn.apply(this, arguments)
timeout = null
}, time);
}
}
}
//时间戳版: 在持续触发事件的过程中,函数会立即执行,并且每x秒执行一次
function throttle(fn, time) {
let previous = 0;
return function() {
let now = Date.now();
let context = this;
let args = arguments;
if (now - previous > time) {
fn.apply(context, args);
previous = now;
}
}
}
//结合版
/**
* @desc 函数节流
* @param func 函数
* @param wait 延迟执行毫秒数
* @param type 1 表时间戳版,2 表定时器版
*/
function throttle(fn, time ,type) {
if(type===1){
var previous = 0;
} else if(type===2){
var timeout;
}
return function() {
let context = this;
let args = arguments;
if(type===1){
let now = Date.now();
if (now - previous > time) {
fn.apply(context, args);
previous = now;
}
}else if(type===2){
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
fn.apply(context, args)
}, time)
}
}
}
}
-
在vue项目中的使用方式
- 自定义指令方式
//结合第三方库Lodash
/**
* 防抖节流
* 1.根据修饰符判断是防抖还是节流
* 使用方法如下
* (1)v-debounceOrThrottle.debounce 防抖
* (2)v-debounceOrThrottle.throttle 节流
* 2.根据传入参数wait、leading、trailing 决定 wait 前后如何触发(可不传使用默认值)
* 使用方法如下
* (1)v-debounceOrThrottle:{wait: 2000,leading:false,trailing:true}.debounce
* @params wait 延迟时间
* @params leading:true(false) 指定调用在[防抖/节流]开始之前 (后)
* @params trailing: true(false) 指定调用在[防抖/节流]开始之后(前)
*/
import _debounce from 'lodash/debounce'
import _throttle from 'lodash/throttle'
let fn = null
export default {
inserted: function(el, binding) {
const { modifiers, arg } = binding
const time = 2000
let _arg = {}
if (arg) _arg = eval('(' + arg + ')')
const { wait = time, leading = true, trailing = false } = _arg
if (modifiers.throttle) {
// 节流
fn = _throttle(binding.value, wait, {
leading,
trailing
})
} else {
// 防抖
fn = _debounce(binding.value, wait, {
leading,
trailing
})
}
el.addEventListener('click', fn)
},
//unbind: 只调用一次,指令与元素解绑时调用
unbind: function(el) {
fn && el.removeEventListener('click', fn)
}
}
- 引用第三方库( lodash)
文档链接:www.lodashjs.com/
防抖函数:_.debounce(func, [wait=0], [options=])#
创建一个 debounced(防抖动)函数,该函数会从上一次被调用后,延迟 wait 毫秒后调用 func 方法。 debounced(防抖动)函数提供一个 cancel 方法取消延迟的函数调用以及 flush 方法立即调用。 可以提供一个 options(选项) 对象决定如何调用 func 方法,options.leading 与|或 options.trailing 决定延迟前后如何触发(注:是 先调用后等待 还是 先等待后调用)。 func 调用时会传入最后一次提供给 debounced(防抖动)函数 的参数。 后续调用的 debounced(防抖动)函数返回是最后一次 func 调用的结果。
注意: 如果 leading 和 trailing 选项为 true, 则 func 允许 trailing 方式调用的条件为: 在 wait 期间多次调用防抖方法。
如果 wait 为 0 并且 leading 为 false, func调用将被推迟到下一个点,类似setTimeout为0的超时
参数
- func (Function): 要防抖动的函数。
- [wait=0] (number): 需要延迟的毫秒数。
- [options=] (Object): 选项对象。
- [options.leading=false] (boolean): 指定在延迟开始前调用。
- [options.maxWait] (number): 设置 func 允许被延迟的最大值。
- [options.trailing=true] (boolean): 指定在延迟结束后调用。
返回
(Function): 返回新的 debounced(防抖动)函数。
vue项目中使用
- 安装依赖
npm i --save lodash
- 按需引入
import _debounce from 'lodash/debounce
- 调用
//点击之后立马调用,一秒内只打印一次
getList: _debounce(
function () { //这里不能用箭头函数 因为箭头函数没有自身的this
console.log("debounce");
},
5000,
{ leading: true, trailing: false }
)
PS:lodash默认leading为false、trailing为true,那么最终的效果是在点击后等待5秒才会打印debounce,即延迟之前不执行函数,而是在延迟之后执行。
handleClick: _debounce(
function(type) {
console.log('_debounce')
this.$emit('getClickResult', type)
},
500,
{ leading: true, trailing: false }
),