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()
})

请记得防护措施不应该影响用户体验。

• 禁用时间不宜过长

• 提示信息要清晰

• 错误处理要完善

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...