今天突然心血来潮想封装防抖,但是发现百度出来的,并不能适用于大部分情况,所以今天自己尝试换一种思路去解决这个问题。
关于传统的方案,主要都有以下问题:
1.this的问题
一般情况下,我们可以直接传入一个可执行函数,防抖函数通过闭包,return一个setTimeout包裹的函数回来。但是vue2中我们写在methods里的方法,是通过this调用的。这种不合适。
function debounce(fn,wait){
var timer = null;
return function(){
if(timer !== null){
clearTimeout(timer);
}
timer = setTimeout(fn,wait);
}
}
2.兼容问题
针对这种问题,我们可以简单改造一下,封装一个vue2版本的。但是这种情况下,我们需要在methods定义多一遍函数,另外vue3的setup中并没有this,因此也不兼容。
// fnName为string
function Vue2Debounce(fnName, time) {
let timeout = null;
return function() {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
this[fnName]();
}, time);
};
}
所以针对以上的情况,我使用promise结合class语法来封装一次。相比其他方案,这个方式特点是,不需要关心用户的参数,执行上下文等情况,原来的逻辑是什么就是什么。只告诉用户,防抖结束。
/**
* vue防抖,兼容vue3,vue2和普通js
* delay:延迟时间(毫秒)
* **/
export default class Debounce {
constructor(delay) {
this.delay = delay ? delay : 500;
this.timeOut = null;
}
debounceEnd (){
return new Promise((resolve,reject) =>{
if(this.timeout){
clearTimeout(this.timeout)
}
this.timeout = setTimeout(()=>{
resolve('success');
},this.delay)
})
}
}
不需要把调用的函数作为参数传进去,仅仅通过使用promise来让我们的代码同步化,达到防抖的效果。
在vue3的setup 中调用。通过new Debounce并传入一个延迟时间。获得一个实例,这个实例返回一个promise的成功回调,如果当前页面有多个不同的操作需要防抖,可以new 多个实例来分别处理。
在页面多中快速连续点击,只触发一次渲染。
在vue2语法中使用
<template>
<div>
<el-button type="success" @click="handleClick('小明')">点击</el-button>
</div>
</template>
<script>
import Debounce from "../../utils/Debounce";
import {reactive, ref} from 'vue'
export default {
name: "index",
data(){
return{
de:null
}
},
created() {
// 我们需要先保存一个防抖实例,不能点击一次new一次
this.de = new Debounce(500)
},
methods: {
async handleClick(name){
await this.de.debounceEnd();
// 500毫秒之后打印小明
console.log(name)
/*your code*/
}
}
}
</script>
<style scoped>
</style>
在原生JS中调用
<body>
<div id="div"></div>
<script src="./Debounce.js"></script>
<script>
let div = document.querySelector('#div');
const de = new Debounce(1000);
div.addEventListener('click',async ()=>{
await de.debounceEnd();
// 一秒后打印
console.log('点击')
});
</script>
</body>
解决问题:
1.不用担心执行上下文的问题
2.不用定义多个函数,结合async,语法还算优雅。
3.兼容vue和原生js
不足的地方:
1.多个地方使用为了保证timeOut独立,需要new多个实例。
2.需要babel兼容低版本浏览器。
…初步测试没有什么问题,所以把这个思路分析给大家,也希望能收获其他更好方式。