前言
项目开发过程中发现,在手速快的情况下,
vue
的组件点击事件会被连续触发多次。这个问题会产生一些意想不到的bug
。
下面我们提供几种解决办法;
一、使用js 防抖配合vue自定义指令实现。
代码如下:
<template>
<div>
<div v-debounce="search" style="width: 100px;height: 100px;background-color: yellow;">快速连续点击</div>
</div>
</template>
<script>
export default {
name: 'debounce',
data () {
return {
msg: 'Welcome to Your Vue.js App',
text: '',
count: 1
}
},
directives: {
debounce: {
inserted: function (el, binding) {
let timer
el.addEventListener('click', () => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
binding.value()
}, 300)
})
}
}
},
methods: {
search () {
// 实际要进行的操作
this.count++
console.log('count is:' + this.count)
}
}
}
</script>
二、控制点击状态
定义一个变量(如:isLock
)来控制按钮的disable
属性,进而来防止用户短时间的多次点击按钮
步骤如下:
- 首先设置变量
isLock
属性值为false
,使得按钮可点击(disable: true
)- 当用户点击后,立即设置按钮不可点击(
disable: false
)- 等待事件执行完或者相应的间隔时间后,恢复按钮为可点击状态
代码如下(示例):
<el-button @click="clickBtn" :disable="isLock">按钮</el-button>
export default{
data(){
return {
isLock: false, // 控制按钮的disable属性
}
},
methods: {
clickBtn(){
// 设置按钮不可点击(disable: false)
this.isLock = true
// 执行耗时间的代码或事件请求等....
// 等待事件执行完或者相应的间隔时间后,恢复按钮为可点击状态
setTimeout(() => {
this.isLock = false
}, 2000)
}
}
}
三、只使用debounce函数
定义common.js
公共方法文件
/**
* 函数防抖
*/
export function debounce(fn, delay) {
// 记录上一次的延时器
var timer = null;
var delay = delay || 200;
return function() {
var args = arguments;
var that = this;
// 清除上一次延时器
clearTimeout(timer)
timer = setTimeout(function() {
fn.apply(that,args)
}, delay);
}
}
vue
组件引入common.js
import {
debounce} from '../common/common.js'
vue
组件代码
<template>
<div>
<div @click="handleClick" style="width: 100px;height: 100px;background-color: yellow;">快速连续点击</div>
</div>
</template>
<script>
import {
debounce,throttle} from '../common/common.js'
export default{
data() {
return {
}
},
methods:{
handleClick:debounce(function(){
console.log('业务逻辑')
},300),
}
}
</script>
<style>
</style>
总结
存在的问题
方法一
、方法三
两种方式存在的问题
方法一
、方法三
两种方式实现的核心逻辑都是使用debounce
的思想,只是使用方式上的不同。
这两种方式已经基本上能满足防重复点击的需求,但实际测试发现延时的时间不好控,如果延时时间短(<150ms)快速点击还是会有几率多次触发事件。延时时间长(>600ms),用户点击后会有个明显的延时过程才能触发事件,用户体验就不太好。
首先明确下我们想要实现的效果。
- 用户在按下按钮的时候立即触发点击事件。
- 用户在快速连续按下按钮的时候只触发第一次的点击事件。
- 用户不间断疯狂点击按钮(暴力测试),也只是立即触发第一次的事件,在疯狂点击的过程中不会触发事件,即使超时时间已经过去。
解决方案
使用 lodash
中的 debounce
;
代码如下:
<template>
<div>
<div @click="handleClick" style="width: 100px;height: 100px;background-color: yellow;">快速连续点击</div>
</div>
</template>
<script>
import _ from "lodash"
export default {
methods: {
handleClick:_.debounce(function() {
//你的业务逻辑
console.log('执行业务逻辑1 ' + Math.random())
}, 1000, {
'leading': true, //在延迟开始前立即调用事件
'trailing': false, //在延时结束后不调用,保证事件只被触发一次
})
}
}
</script>
<style>
</style>
lodash
的debounce
的关键配置
就是 leading
, trailing
关于这两个属性的具体解释可以参照lodash官方文档。