防抖与节流的区别
防抖与节流达到的目的都是一段时间内只触发一次函数执行,简单阐述防抖与节流的区别:
- 节流:在一段时间内多次触发,只执行一次,且一般是在刚开始触发时执行这一次函数。如滑动窗口时触发的操作。
- 防抖:在一段时间内多次触发,只执行最后一次。如付款操作,多次点击只将最后一次点击提交。
4种通用的节流与防抖的方法(返回一个方法,不能直接执行)
这四种方法为网上大多数人提供的方法,这里只做汇总,可能并不适用于你的项目代码。
// js文件,可抽取进utils
// 节流函数
// 会先立即执行一次,时间戳写法
const throttle = (func, delay = 1000) => {
//距离上一次的执行时间
let lastTime = 0;
//不能用箭头函数
return function () {
let context = this
let args = arguments
let now = new Date().getTime()
//如果距离上一次执行超过了delay才能再次执行
if(now - lastTime > delay){
func.apply(context,args)
lastTime = now
}
}
}
// 到delay时间后才开始执行第一次
const throttle2 = function(func, delay) {
let timeout;
return function() {
let context = this;
let args = arguments;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, delay)
}
}
}
// 防抖函数
// 会先立即执行一次
const debounce = (func, delay = 1000, immediate = true) => {
// 计时器,用来记录何时结束
let timer = null
//不能用箭头函数
return function () {
let context = this;
let args = arguments;
if (timer) {
clearTimeout(timer)
}
// 先立即执行一次
console.log(immediate, timer)
if (immediate && !timer) {
func.apply(context,args)
}
timer = setTimeout(() => {
func.apply(context,args)
}, delay)
}
}
// 到delay时间后才会执行
const debounce2 = function (func, delay) {
let timer;
return function() {
let context = this;
let args = arguments;
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
func.apply(context, args)
}, delay)
}
}
module.exports = {
throttle,
throttle2,
debounce,
debounce2
}
代码内引用
<template>
<div>
<el-button @click="throttlePrint">点击输出1</el-button>
<el-button @click="throttlePrint2">点击输出2</el-button>
<el-button @click="debouncePrint">点击输出3</el-button>
<el-button @click="debouncePrint2">点击输出4</el-button>
</div>
</template>
<script>
import { throttle, throttle2, debounce, debounce2 } from "@/utils/test";
export default {
data() {
return {};
},
methods: {
print() {
console.log("this.$test"); // 去控制台看输出时机
},
throttlePrint: throttle(function () {
this.print();
}, 2000),
throttlePrint2: throttle2(function () {
this.print();
}, 2000),
debouncePrint: debounce(function () {
this.print();
}, 2000),
debouncePrint2: debounce2(function () {
this.print();
}, 2000),
},
};
</script>
<style></style>
上述方法全部为返回一个function,不直接执行,下面提供直接可用的两种方法
Vue uni-app内可以直接调用的节流与防抖方法
节流:
/**
* 节流原理:在一定时间内,只能触发一次
*
* @param {Function} func 要执行的回调函数
* @param {Number} wait 延时的时间
* @param {Boolean} immediate 是否立即执行
* @return null
*/
let timer, flag;
function throttle(func, wait = 500, immediate = true) {
if (immediate) {
if (!flag) {
flag = true;
// 如果是立即执行,则在wait毫秒内开始时执行
typeof func === 'function' && func();
timer = setTimeout(() => {
flag = false;
}, wait);
}
} else {
if (!flag) {
flag = true
// 如果是非立即执行,则在wait毫秒内的结束处执行
timer = setTimeout(() => {
flag = false
typeof func === 'function' && func();
}, wait);
}
}
};
export default throttle
防抖:
/**
* 防抖原理:在一定时间内,只触发最后一次
*
* @param {Function} func 要执行的回调函数
* @param {Number} delay 延时的时间
* @param {Boolean} immediate 是否立即执行
* @return null
*/
let timer;
function debounce(func, wait = 300, immediate = false) {
if(timer){
clearTimeout(timer);
}
if(immediate){
typeof func === 'function' && func();
}
timer = setTimeout(() => {
typeof func === 'function' && func();
}, wait);
}
使用方法:
import { throttle, debounce } from '@/common/utils/xxxx';
methods:{
certainFunctions(args){
throttle(() => {
console.log(args)
// do something
}, 1000);
debounce(() => {
console.log(args)
// do something
}, 1000)
}
}
onLoad(){
this.certainFunctions(args);
}
2种通用的节流与防抖的方法(可直接执行)
static _lastExecTimes = new Map()
static throttle(func, delay = 300, ...args) {
const now = Date.now();
const funcKey = func.toString(); // 使用函数字符串作为唯一标识
// 如果该函数从未执行过,或者距离上次执行已超过延迟时间
if (!this._lastExecTimes.has(funcKey)) {
this._lastExecTimes.set(funcKey, 0); // 初始化
}
const lastExecTime = this._lastExecTimes.get(funcKey);
if (now - lastExecTime >= delay) {
func.apply(this, args);
this._lastExecTimes.set(funcKey, now);
}
// 如果未达到延迟时间,则不执行任何操作
}
static _debounceTimers = new Map()
static debounce(func, wait = 300, immediate = false, ...args) {
const funcKey = func.toString();
// 清理方法
const cleanup = () => {
if (this._debounceTimers.has(funcKey)) {
clearTimeout(this._debounceTimers.get(funcKey));
this._debounceTimers.delete(funcKey);
}
};
cleanup(); // 先清理现有的
if (immediate && !this._debounceTimers.has(funcKey)) {
func.apply(this, args);
}
this._debounceTimers.set(
funcKey,
setTimeout(() => {
if (!immediate) {
func.apply(this, args);
}
cleanup();
}, wait)
);
}
使用方法:
// 按钮点击事件,方法会直接执行
const btnClick = () => {
Toolkit.throttle(() => { // Toolkit为方法所属的类
console.error("按钮有效")
}, 900)
}
注意事项
节流与防抖因为使用了apply改变了函数的执行上下文,也就是this指向,因此方法内要时刻留意this的指向问题,比如不能使用 箭头函数 ,因为箭头函数会默认将this改变为该函数所在的作用域指向的对象,而不是使用时所在的作用域指向的对象。