防止重复点击调取接口

场景

在项目中遇到过这样一个场景,点击提交表单的按钮之后,发现没有任何反应,随后又点击了一次,造成数据的重复提交,在数据库中出现了两条一模一样的数据。

原因

由于axios提交数据是异步提交,点击提交按钮是向后端异步请求,发送请求后后端返回数据需要时间处理,如果第一次点击的请求尚未完成,又接二连三地提交了几次,同时后面发送的请求都被后台处理了,这种情况如果是读取数据不会有太大影响,但是涉及到数据提交保存或者提交之后多表数据处理就麻烦了,而且此类现象造成的数据都不正常,所以此种情况务必要避免。

解决办法

防抖

原理:当第一次触发点击事件时,不会立即执行接口,会等待监听一段时间,若在这段时间内没有再次触发该事件,等待时间过后就会执行接口。倘若等待时间期间监听到又点击了一次,等待时间随机重置,从最后一次点击时间开始再重新等待这段时间,等待结束后再执行接口。

步骤:
1.创建防抖全局js方法

//debounce.js
export const debounce=(()=>{
	let timer=null
	return (callback,wait)=>{
	clearTimeout(timer)
	timer=setTimeout(callback,wait)
	}
})

2.具体引用

//test.vue
<script>
import {debounce} from "./debounce"
export default{
name:'test'
data(){
return{}
},
methods:{
getMovieName(e){
	debounce(()=>{
		console.log("防抖成功")
	},1000)
}
}
}
</script>

案例
在这里插入图片描述

节流

原理:当持续触发事件时,保证一定时间段内只调用一次事件处理函数。
**步骤:**

按钮禁用控制或者loading

// 按钮
<el-button @click="save"  :disabled="disabledFlag">保 存</el-button>

save() {
// 调用接口之前禁用按钮,防止多次点击调用接口
          this.disabledFlag = true
          // 或者打开loading等待
          this.loading.show()
          // 调用接口进行添加填报数据
          api.save({ id: this.id }).then(res => {
            // 接口返回后
            this.disabledFlag = false
            this.loading.hide()
            this.$message({
              type: 'success',
              message: "保存成功!"
            })
          }).catch(() => {
            this.disabledFlag = false
            this.loading.hide()
          })
}
<think>我们使用axios调用登录接口,并处理响应数据。根据用户提供的vite.config.js中的代理配置,我们可以将请求转发到后端接口。 用户已经配置了代理,将'/api'开头的请求转发到'http://110.40.173.100:5173',但是我们的接口是'/dev-api/login',所以我们需要调整代理配置,使其匹配/dev-api路径。 但是,用户当前的代理配置是: proxy: { '/api': { target: 'http://110.40.173.100:5173', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } } 而我们的接口是`http://172.26.26.43/dev-api/login`,因此我们需要修改代理配置,将/dev-api的请求转发到目标服务器。 建议修改vite.config.js的代理配置如下: ```javascript // vite.config.js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { proxy: { '/dev-api': { // 匹配以/dev-api开头的请求 target: 'http://172.26.26.43', // 目标服务器地址 changeOrigin: true, rewrite: (path) => path.replace(/^\/dev-api/, '') // 将路径中的/dev-api去掉 } } } }) ``` 这样,前端在请求`/dev-api/login`时,会被代理到`http://172.26.26.43/login`。 然后,我们在登录组件中使用axios发送请求到`/dev-api/login`(因为代理只对开发环境有效,生产环境需要配置nginx等)。 修改登录组件的handleLogin方法: 步骤: 1. 安装axios: `npm install axios` 2. 在组件中引入axios 3. 发送POST请求,携带用户名和密码 4. 处理响应,存储token,并跳转到详情页 同时,为了更好的用户体验,我们添加一个加载状态,防止重复提交。 修改后的代码: ```vue <template> <div class="login-container"> <div class="login-form"> <h2>用户登录</h2> <input v-model="username" placeholder="用户名"> <input v-model="password" type="password" placeholder="密码"> <button @click="handleLogin" :disabled="loading"> {{ loading ? '登录中...' : '登录' }} </button> <router-link to="/register">注册账号</router-link> </div> </div> </template> <script setup> import { ref } from 'vue' import { useRouter } from 'vue-router' import axios from 'axios' const router = useRouter() const username = ref('') const password = ref('') const loading = ref(false) const handleLogin = async () => { if (!username.value || !password.value) { alert('请输入用户名和密码') return } loading.value = true try { // 发送POST请求到登录接口(注意:这里使用了代理,所以写本地路径/dev-api/login) const response = await axios.post('/dev-api/login', { username: username.value, password: password.value }) // 假设后端返回的数据结构为:{ code: 200, message: '成功', data: { token: 'xxx' } } if (response.data.code === 200) { // 存储token localStorage.setItem('token', response.data.data.token) // 跳转到详情页,这里假设详情页的路由名为'Detail',并且需要传递参数id router.push({ name: 'Detail', params: { id: 123 } }) } else { // 登录失败,显示错误信息 alert(`登录失败: ${response.data.message}`) } } catch (error) { // 请求失败,可能是网络错误或服务器错误 console.error('登录请求失败:', error) alert('登录请求失败,请检查网络连接或稍后再试') } finally { loading.value = false } } </script> <style scoped> /* 原有样式保持不变 */ </style> ``` 注意: 1. 代理配置修改后需要重启开发服务器。 2. 生产环境部署时,需要确保前端请求的地址正确,可以通过环境变量来配置基础URL。 关于token传递到详情页: 我们将token存储在了localStorage中,在详情页组件中可以通过`localStorage.getItem('token')`来获取。但是,更推荐的做法是使用Vuex或Pinia来管理用户状态,或者通过路由参数传递(但token较长,不适合通过URL传递)。 在详情页获取token的示例(使用localStorage): ```vue <script setup> import { onMounted } from 'vue' onMounted(() => { const token = localStorage.getItem('token') console.log('token:', token) // 使用token进行后续操作,比如请求需要认证的接口 }) </script> ``` 但是,请注意:敏感信息不建议放在localStorage中,因为容易受到XSS攻击。如果应用安全性要求较高,可以考虑使用HttpOnly的cookie来存储token,或者使用sessionStorage(关闭浏览器标签页后失效)。 另外,在发送需要认证的请求时,通常需要在请求头中添加token,例如: ```javascript axios.get('/api/userinfo', { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }) ``` 相关问题: 1. 如何解决Vue项目中的跨域问题? 2. 如何在Vue中管理用户认证状态? 3. 如何安全地存储JWT token? 4. 如何实现路由守卫,在未登录时跳转到登录页? 5. 如何配置axios的全局实例,统一处理请求和响应?</think>### Vue前端登录接口对接与Token传递方案 根据你的需求,我将提供完整的解决方案,包括接口调用、Token存储和传递到详情页的实现。 #### 步骤1:安装Axios库 ```bash npm install axios ``` #### 步骤2:配置代理解决跨域问题(vite.config.js) ```javascript import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { proxy: { '/dev-api': { target: 'http://172.26.26.43', // 后端服务器地址 changeOrigin: true, rewrite: (path) => path.replace(/^\/dev-api/, '') } } } }) ``` #### 步骤3:修改登录组件代码 ```vue <template> <div class="login-container"> <div class="login-form"> <h2>用户登录</h2> <input v-model="username" placeholder="用户名"> <input v-model="password" type="password" placeholder="密码"> <button @click="handleLogin" :disabled="loading"> {{ loading ? '登录中...' : '登录' }} </button> <router-link to="/register">注册账号</router-link> </div> </div> </template> <script setup> import { ref } from 'vue' import { useRouter } from 'vue-router' import axios from 'axios' const router = useRouter() const username = ref('') const password = ref('') const loading = ref(false) const handleLogin = async () => { if (!username.value || !password.value) { alert('请输入用户名和密码') return } loading.value = true try { const response = await axios.post('/dev-api/login', { username: username.value, password: password.value }) // 处理响应数据(根据实际API结构调整) if (response.data.code === 200) { const token = response.data.data.token localStorage.setItem('token', token) // 跳转到详情页并传递token router.push({ name: 'Detail', params: { id: 123, token: token // 直接传递token(可选) } }) } else { alert(`登录失败: ${response.data.message}`) } } catch (error) { console.error('登录请求失败:', error) alert('登录请求失败,请检查网络连接') } finally { loading.value = false } } </script> <style scoped> /* 原有样式保持不变 */ button:disabled { opacity: 0.7; cursor: not-allowed; } </style> ``` #### 步骤4:在详情页组件中获取Token ```vue <!-- Detail.vue --> <script setup> import { onMounted } from 'vue' import { useRoute } from 'vue-router' const route = useRoute() onMounted(() => { // 方式1:从路由参数获取(如果通过路由传递) const tokenFromRoute = route.params.token console.log('Token from route:', tokenFromRoute) // 方式2:从localStorage获取(推荐) const tokenFromStorage = localStorage.getItem('token') console.log('Token from storage:', tokenFromStorage) // 使用token请求需要认证的接口 if (tokenFromStorage) { fetchUserData(tokenFromStorage) } }) const fetchUserData = async (token) => { try { const response = await axios.get('/dev-api/user-info', { headers: { Authorization: `Bearer ${token}` } }) console.log('用户数据:', response.data) } catch (error) { console.error('获取用户数据失败:', error) } } </script> ``` #### 关键点说明 1. **Token存储方案**: - 使用`localStorage`持久化存储Token(页面刷新后仍可用) - 对于敏感数据,可改用`sessionStorage`(浏览器关闭后失效) - 路由参数传递适合短期、非敏感数据 2. **API请求认证**: ```javascript // 在需要认证的请求中添加Authorization头 axios.get('/api/protected', { headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } }) ``` 3. **路由守卫保护**(router/index.js): ```javascript import { createRouter } from 'vue-router' const router = createRouter({ ... }) router.beforeEach((to) => { if (to.meta.requiresAuth && !localStorage.getItem('token')) { return { name: 'Login' } } }) ``` #### 常见问题解决方案 1. **跨域问题**: - 确保后端设置了CORS头部:`Access-Control-Allow-Origin: *` - 在Vite代理配置中正确设置`changeOrigin: true`[^1] 2. **网络错误处理**: ```javascript try { // API请求 } catch (error) { if (error.response) { // 服务器返回4xx/5xx错误 console.error('API错误:', error.response.status) } else if (error.request) { // 请求已发出但无响应 console.error('网络错误:', error.message) } else { // 其他错误 console.error('请求配置错误:', error.message) } } ``` 3. **Token安全建议**: - 设置合理过期时间 - 使用HTTPS传输 - 避免在控制台打印敏感Token - 实现Token刷新机制 #### 相关问题 1. 如何实现Vue中的全局请求拦截器? 2. JWT Token过期后如何自动刷新? 3. 如何在Vue中安全地存储敏感信息? 4. 如何处理API返回的401未授权错误? 5. 如何实现Vue应用中的多环境配置(开发/生产)? 6. 如何优化Axios的错误处理机制?[^1][^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值