方法1:axios
利用了axios拦截器,每次请求拦截的时候,检查请求的地址在不在list里,不在的话才允许发请求并把这次请求的地址存下来,在的话不允许发请求。每次响应拦截的时候,从list里去除这次请求地址。
附加功能:1、部署了getpost方法,简化使用。2、请求成功的时候,延缓2秒去除,即请求成功了就不许再发请求了。
坑点:在请求拦截时去除掉的请求也会进入响应拦截,因此要区别处理。
//文件:api/http.js
import axios from 'axios'
axios.defaults.timeout = 5000; //响应时间
axios.defaults.baseURL = 'http://localhost:3000'; //配置接口地址
let reqList = []
/**
* 阻止重复请求
* @param {array} reqList - 请求缓存列表
* @param {string} url - 当前请求地址
* @param {function} cancel - 请求中断函数
* @param {string} errorMessage - 请求中断时需要显示的错误信息
*/
const stopRepeatRequest = function (reqList, url, cancel, errorMessage) {
const errorMsg = errorMessage || ''
for (let i = 0; i < reqList.length; i++) {
if (reqList[i] === url) {
cancel(errorMsg)
return
}
}
reqList.push(url)
}
/**
* 允许某个请求可以继续进行
* @param {array} reqList 全部请求列表
* @param {string} url 请求地址
*/
const allowRequest = function (reqList, url) {
for (let i = 0; i < reqList.length; i++) {
if (reqList[i] === url) {
reqList.splice(i, 1)
break
}
}
}
//POST传参序列化(添加请求拦截器)
axios.interceptors.request.use((config) => { //在发送请求之前做某件事
// //携带请求头
// let token = window.localStorage.getItem("accessToken")
// console.log("token:"+token);
// config.headers.accessToken = token;//下面两种方式都行
// // config.headers['accessToken'] = token
// 设置cancelToken对象
let cancel
config.cancelToken = new axios.CancelToken(c => cancel = c)
// 阻止重复请求。当上个请求未完成时,相同的请求不会进行
stopRepeatRequest(reqList, config.url, cancel, `${config.url} 请求被中断`)
return Promise.resolve(config)
},(error) =>{
return Promise.reject(error)
});
//返回状态判断(添加响应拦截器)
axios.interceptors.response.use((res) =>{ //对响应数据做些事
// 增加延迟,相同请求不得在短时间内重复发送
setTimeout(() => {
allowRequest(reqList, res.config.url)
}, 1000)
return Promise.resolve(res)
}, (error) => {
if (axios.isCancel(error)) { // 区分错误是因为被防抖阻止还是404
console.log(error.message)
}else{
// 取消了延迟,相同请求不得在短时间内重复发送,除非上一次的请求404了
allowRequest(reqList, error.config.url)
}
return Promise.reject(error)
})
//返回一个Promise(发送post请求)
export function fetchPost(url, params) {
return new Promise((resolve, reject) => {
axios.post(url, params)
.then(response => {
resolve(response)
}, err => {
reject(err)
})
.catch((error) => {
reject(error)
})
})
}
// 返回一个Promise(发送get请求)
export function fetchGet(url, param) {
return new Promise((resolve, reject) => {
axios.get(url, {params: param})
.then(response => {
resolve(response.data)
try{console.log(response.data)}
catch{console.log('无数据')}
}, err => {
reject(err)
})
.catch((error) => {
reject(error)
})
})
}
export default {
fetchPost,
fetchGet,
}
//文件:main.js
+++++++++
import {fetchPost, fetchGet} from '@/api/http'
Vue.prototype.post = fetchPost;
Vue.prototype.get = fetchGet;
//文件:App.vue
++++++
this.get('/posts/1').then(() => {}).catch(() => {})
方法2:element组件
使用el-button的loading属性, 如果使用loading 属性, 通常 我们会在vue的data中定义一个loading变量,然后调用ajax之前将loading设置为true, ajax返回后将loading设置为false. 这是比较合理的办法,但是如果一个系统非常多这种情况, 那我们需要每一个页面都去定义一个loading变量,作为一个合格的程序员,我们当然想不要写这么多重复的代码,这时候就需要对el-button做二次封装了
附加功能:1、二次封装了el-button
//文件:el-el-button
<template>
<!-- v-bind=$attrs,这个表示是继承所有父组件传过来的属性,用了这个属性,我们可以将el-button的所有的props都能继承 -->
<el-button v-bind="$attrs" :loading="loadingStatus" @click="handleClick">
<slot/>
</el-button>
</template>
<script>
export default {
name: 'el-el-button',
props: {
autoLoading: {
type: Boolean,
default: false
}
},
data() {
return {
loadingStatus: false
}
},
methods: {
handleClick() {
if (this.autoLoading) {
this.loadingStatus = true
}
// 控制loading消失,在业务组件上处理好业务逻辑以后后执行这个回调函数,就能消失loading了
this.$emit('click', () => {
this.loadingStatus = false
})
}
}
}
</script>
<style>
</style>
</script>
<style>
</style>
//文件:App.vue
++++++
<el-el-button :auto-loading="true" @click="submit">自动loading按钮</el-el-button>
++++++
submit(done) {
// 这里供业务组件处理一些事情,比如ajax请求,此处用setTimeout模拟, 执行done()方法消失loading
setTimeout(() => {
done()
}, 1000)
},
//文件:index.js
import button from './button.vue';
const components = [button]
const elEl = {
install:function(Vue) {
components.forEach(component => {
Vue.component(component.name, component);
})
}
}
export default elEl;
//文件;main.js
++++++
import elEl from '@/components/elEl/index.js'
Vue.use(elEl);
方法3:全局指令
加一个指令,这个指令做到提交以后禁用按钮一段时间,防止重复提交。这里也做了全局部署
//文件:noMoreClick.js
const noMoreClick = {
name:'noMoreClick',
inserted(el) {
el.addEventListener('click', () => {
el.classList.add('is-disabled')
el.disabled = true
setTimeout(() => {
el.disabled = false
el.classList.remove('is-disabled')
}, 2000)
})
}
}
export default noMoreClick
文件:index.js
import noMoreClick from './packge/noMoreClick.js'
const arr = [noMoreClick]
const utils = {
install:function(Vue) {
arr.forEach(e => {
Vue.directive(e.name, e);
})
}
}
export default utils;
//文件main.js
import directive from '@/directive/index.js'
++++++++
Vue.use(directive);
//文件:App.vue
<el-button v-no-more-click>确 定</el-button>
参考:
2.https://juejin.cn/post/6916474993635229704
3.https://juejin.cn/post/6948621847239852062