# Vue中的防抖

# 问题萌生

近日在项目中,在测试环境中,某个页面列表里,提交表单的按钮被测试多次点击,报出接口请求多次导致数据记录错误的问题。

# 解决方案

使用最熟悉的防抖或者节流来做。一开始考虑用节流,设置2秒延迟,但考虑到测试会一直点击,怕2秒后调用接口的期间会点击出第二次调用。所以选择了防抖来做。

# 重温防抖

// 防抖函数
function debounce(callback, delay) {
  let timeout;
  return function (args) {
    let that = this; // 保存this指向
    let _args = args; // 保存argumens参数组
    clearTimeout(timeout); // 每次触发防抖都将清除旧命令
    
    timeout = setTimeout(() => {
      callback.call(that, _args);
    }, delay);
  };
}

# 注意事项

在vue中如果直接将目标函数放入防抖函数内,则会报错。

<template>
	<!-- 显示内容 -->
</template>

<script>
import debounce from '@/utils/debounce';
    export default {
        methods:{
            async getData(){
                let res = await fetch("xxx");
                const {data} = res;
                console.log(data);
            },
            debounceGetData:debounce(getData,2000), //error: getData is not defined
            debounce(this.getData,2000), //同样error: getData is not defined
        }
    }
</script>

# 报错原因

debounce参数里的作用域中,this指向不是Vue.component全局。因此会报错该getData方法未定义。

# 解决方法

让debounce中调用传入函数时,this指向全局。

// Vue使用的防抖函数
function debounce(fnName, delay) {
 let timeout=null;
  return function () {
    timeout && clearTimeout(timeout); // 每次触发防抖都将清除旧命令
    timeout = setTimeout(() => {
      this[fnName](); // 此时箭头函数指向调用它的最近一层的普通函数,若没有外层普通函数。则指向全局对象(Vue.component)
    }, delay);
  };
}
export default debounce;

<template>
	<!-- 显示内容 -->
</template>

<script>
import debounce from '@/utils/debounce';
    export default {
        methods:{
            async getData(){
                let res = await fetch("xxx");
                const {data} = res;
                console.log(data);
            },
            debounceGetData:debounce("getData",2000), //运行成功
        }
    }
</script>

# 总结

在Vue中使用这类函数时,注意传入函数后,看看函数内部是否有调用Vue中methods的函数。若有,则需要注意this箭头指向,除了上述那样写到,也可把全局this作为参数传入debounce,也可解决未定义问题,否则将引起报错。