Vue3项目如何防止用户重复提交
#奋进江城共筑新篇#
字数 888,阅读大约需 5 分钟
Vue3项目如何防止用户重复提交
用户重复提交是一个常见问题。用户点击按钮后没有立即看到反馈,会再次点击。这导致重复请求,增加服务器压力,可能产生重复数据。
为什么需要防止重复提交
防止重复提交有多个好处。
提升用户体验。用户知道操作已经生效,不会困惑。
减轻服务器负担。减少不必要请求,节省资源。
避免数据错误。重复提交可能产生重复订单或重复记录。
常见重复提交场景
重复提交发生在这些情况。
• 用户快速点击提交按钮
• 页面响应慢,用户以为没点中
• 网络延迟,请求未立即发送
• 表单提交后跳转延迟
解决方案
在Vue3中可以用多种方式防止重复提交。下面介绍几种实用方法。
按钮禁用方案
最简单的方法是点击后禁用按钮。
<template>
<button @click="handleSubmit" :disabled="isSubmitting">
{{ isSubmitting ? '提交中...' : '提交' }}
</button>
</template>
<script setup>
import { ref } from 'vue'
const isSubmitting = ref(false)
const handleSubmit = async () => {
if (isSubmitting.value) return
isSubmitting.value = true
try {
await submitForm()
} finally {
isSubmitting.value = false
}
}
</script>
这种方法简单有效。用户看到按钮状态变化,知道操作已触发。
请求锁方案
多个组件可能触发一样请求。这时需要全局请求锁。
// utils/submitLock.js
const pendingRequests = new Set()
export const addRequest = (requestId) => {
if (pendingRequests.has(requestId)) {
return false
}
pendingRequests.add(requestId)
return true
}
export const removeRequest = (requestId) => {
pendingRequests.delete(requestId)
}
export const checkRequest = (requestId) => {
return pendingRequests.has(requestId)
}
在组件中使用:
<script setup>
import { addRequest, removeRequest } from '@/utils/submitLock'
const handleSubmit = async (requestId) => {
if (!addRequest(requestId)) {
alert('请求已提交,请勿重复操作')
return
}
try {
await submitData()
} finally {
removeRequest(requestId)
}
}
</script>
防抖函数方案
防抖函数确保在必定时间内只执行一次。
// utils/debounce.js
export const debounce = (func, wait) => {
let timeout
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout)
func(...args)
}
clearTimeout(timeout)
timeout = setTimeout(later, wait)
}
}
在Vue3中使用:
<script setup>
import { debounce } from '@/utils/debounce'
const handleSubmit = debounce(async () => {
await submitForm()
}, 1000)
</script>
进阶防护方案
基础方案能解决大部分问题。复杂场景需要更高级方案。
请求拦截器方案
axios拦截器可以统一处理重复请求。
// utils/request.js
import axios from 'axios'
const pendingMap = new Map()
const generateReqKey = (config) => {
const { method, url, params, data } = config
return [method, url, JSON.stringify(params), JSON.stringify(data)].join('&')
}
const addPending = (config) => {
const key = generateReqKey(config)
config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => {
if (!pendingMap.has(key)) {
pendingMap.set(key, cancel)
}
})
}
const removePending = (config) => {
const key = generateReqKey(config)
if (pendingMap.has(key)) {
const cancel = pendingMap.get(key)
cancel(key)
pendingMap.delete(key)
}
}
const instance = axios.create()
instance.interceptors.request.use(config => {
removePending(config)
addPending(config)
return config
})
instance.interceptors.response.use(response => {
removePending(response.config)
return response
}, error => {
removePending(error.config)
return Promise.reject(error)
})
export default instance
路由守卫方案
页面跳转时,可能还有未完成的请求。路由守卫可以处理这种情况。
// router/guards.js
import { getCurrentInstance } from 'vue'
const pendingRequests = []
export const addPendingRequest = (request) => {
pendingRequests.push(request)
}
export const removePendingRequest = (request) => {
const index = pendingRequests.indexOf(request)
if (index > -1) {
pendingRequests.splice(index, 1)
}
}
export const clearPendingRequests = () => {
pendingRequests.forEach(request => {
request.cancel && request.cancel()
})
pendingRequests.length = 0
}
在路由配置中使用:
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import { clearPendingRequests } from './guards'
const router = createRouter({
history: createWebHistory(),
routes: [...]
})
router.beforeEach((to, from, next) => {
clearPendingRequests()
next()
})
请记得防护措施不应该影响用户体验。
• 禁用时间不宜过长
• 提示信息要清晰
• 错误处理要完善
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...
