一、前端防重复点击(主策略)
1.1 禁用按钮法(最常见)
<button :disabled="loading" @click="handleClick">提交</button>
methods: {
async handleClick() {
if (this.loading) return;
this.loading = true;
try {
const res = await this.submitRequest();
// 成功处理
} catch (err) {
// 错误处理
} finally {
this.loading = false;
}
}
}
1.2 防抖处理(适用于非强制立即提交的按钮)
若用户短时间内多次点击,以最后一次为准,适合搜索框、查询按钮等。
import _ from 'lodash';
methods: {
handleClick: _.debounce(function () {
this.doSubmit();
}, 1000)
}
1.3 节流处理(适用于固定间隔执行,比如每10秒一次)
handleClick: _.throttle(function () {
this.doSubmit();
}, 10000)
1.4 全局指令封装(Vue 示例)
Vue.directive('preventReClick', {
inserted(el, binding) {
el.addEventListener('click', () => {
if (!el.disabled) {
el.disabled = true;
setTimeout(() => {
el.disabled = false;
}, binding.value || 1000); // 默认1秒内禁止重复点击
}
});
}
});
✅ 二、后端幂等控制(辅助策略)
前端只能减少重复请求,不能绝对防止,真正重要的业务必须后端也防重复执行。
2.1 幂等 Token(业务允许的话可以,如果有单点登录等需求则不建议)
-
前端向服务端获取一个
一次性token -
提交请求时携带这个 token
-
后端校验 token 有效性,使用过的 token 拒绝再次执行
2.2 请求唯一标识 + Redis 锁控制(适用于秒杀、支付等) 推荐
简单实现
-
请求携带业务唯一标识(如订单号、签约ID)
-
后端用 Redis 锁控制:(分布式锁)
// Java示意
if (!redis.setIfAbsent("LOCK_KEY_" + businessId, 1, 5秒)) {
throw new RepeatOperationException("请勿重复提交");
}
Boolean setIfAbsent(K key, V value, long timeout, TimeUnit unit)
表示:如果 key 不存在,则设置 key 并返回 true;如果已存在,则不修改,返回 false。
但是更推荐用Redisson(分布式锁)
🔐 什么是分布式锁?
在多个进程或服务器之间,保证同一份资源只能被一个线程/节点访问或操作。
🔒 你手写锁 vs Redisson 功能对比
| 功能 | 你用 setIfAbsent() | Redisson |
|---|---|---|
| 锁唯一性校验 | ❌ 需要你自己加 UUID + 比较 | ✅ 内部已实现 |
| 解锁安全性 | ❌ 删除可能删到别人加的锁 | ✅ 只能删掉自己加的锁 |
| 可重入 | ❌ 无 | ✅ 有 |
| 自动续期 | ❌ 锁一旦过期就没了 | ✅ 自动续命,不会中途释放 |
| 锁等待 | ❌ 无法等待锁,只能立即返回 | ✅ 可以设置等待时间 |
| 超时释放 | ✅ 你可以手动设置 | ✅ 自动处理 |
✅ Redisson 分布式锁示例代码
@Autowired
private RedissonClient redissonClient;
public void doBusiness(String businessId) {
RLock lock = redissonClient.getLock("LOCK:BUSINESS:" + businessId);
boolean success = lock.tryLock(5, 30, TimeUnit.SECONDS);
// 参数说明:
// 5 秒内尝试获取锁(等待时间)
// 加锁成功后 30 秒后自动释放(加锁时间)
if (!success) {
throw new BusinessException("系统繁忙,请稍后再试");
}
try {
// 你的核心业务逻辑
} finally {
lock.unlock(); // 安全释放锁
}
}
✅ Redisson 依赖和配置
<!-- Maven 依赖 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.23.5</version> <!-- 根据你的 Spring Boot 版本选择 -->
</dependency>
# application.yml
redisson:
config: |
singleServerConfig:
address: "redis://127.0.0.1:6379"
🧠 总结一句话:
用
RedisTemplate写锁,就像用裸线程写多线程程序;
用Redisson写锁,就像用线程池 + Future 写多线程,高效、安全、可维护。

被折叠的 条评论
为什么被折叠?



