最简单,最优雅的方式解决防抖问题,兼容Vue3,Vue2和原生JS。

1 篇文章 0 订阅
1 篇文章 0 订阅

今天突然心血来潮想封装防抖,但是发现百度出来的,并不能适用于大部分情况,所以今天自己尝试换一种思路去解决这个问题。

关于传统的方案,主要都有以下问题:

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兼容低版本浏览器。

…初步测试没有什么问题,所以把这个思路分析给大家,也希望能收获其他更好方式。

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值