vue阻止重復(fù)請(qǐng)求實(shí)現(xiàn)示例詳解
背景
項(xiàng)目當(dāng)中前端代碼會(huì)遇到同一個(gè)請(qǐng)求向服務(wù)器發(fā)了多次的情況,我們要避免服務(wù)器資源浪費(fèi),同一個(gè)請(qǐng)求一定時(shí)間只允許發(fā)一次請(qǐng)求
思路
(1)如果業(yè)務(wù)簡(jiǎn)單,例如同一個(gè)按鈕防止多次點(diǎn)擊,我們可以用定時(shí)器做防抖處理
(2)如果業(yè)務(wù)復(fù)雜,例如多個(gè)組件通過(guò)代碼,同一個(gè)請(qǐng)求發(fā)多次,這個(gè)時(shí)候防抖已經(jīng)不好處理了,最好是對(duì)重復(fù)的ajax請(qǐng)求統(tǒng)一做取消操作
方式1-通過(guò)定時(shí)器做防抖處理
(a)概述
效果:當(dāng)用戶(hù)連續(xù)點(diǎn)擊多次同一個(gè)按鈕,最后一次點(diǎn)擊之后,過(guò)小段時(shí)間后才發(fā)起一次請(qǐng)求
原理:每次調(diào)用方法后都產(chǎn)生一個(gè)定時(shí)器,定時(shí)器結(jié)束以后再發(fā)請(qǐng)求,如果重復(fù)調(diào)用方法,就取消當(dāng)前的定時(shí)器,創(chuàng)建新的定時(shí)器,等結(jié)束后再發(fā)請(qǐng)求,工作當(dāng)中可以用第三方封裝的工具函數(shù)例如lodash
的debounce
方法來(lái)簡(jiǎn)化防抖的代碼
(b)代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/lodash.js/4.17.21/lodash.min.js" type="application/javascript"></script> <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js" type="application/javascript"></script> <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js" type="application/javascript"></script> </head> <body> <div id="app"> <button @click="onClick">請(qǐng)求</button> </div> </body> <script> // 定義請(qǐng)求接口 function sendPost(data){ return axios({ url: 'https://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test', method: 'post', data }) } new Vue({ el: '#app', methods: { // 調(diào)用lodash的防抖方法debounce,實(shí)現(xiàn)連續(xù)點(diǎn)擊按鈕多次,0.3秒后調(diào)用1次接口 onClick: _.debounce(async function(){ let res = await sendPost({username:'zs', age: 20}) console.log('請(qǐng)求的結(jié)果', res.data) }, 300), }, }) </script> </html>
(c)預(yù)覽
(d)存在的問(wèn)題
無(wú)法解決多個(gè)按鈕件的重復(fù)請(qǐng)求的發(fā)送問(wèn)題,例如下面兩種情況
情況-在點(diǎn)擊事件上做防抖
按鈕事件間是相互獨(dú)立的,調(diào)用的是不同方法,做不到按鈕間防抖效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/lodash.js/4.17.21/lodash.min.js" type="application/javascript"></script> <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js" type="application/javascript"></script> <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js" type="application/javascript"></script> </head> <body> <div id="app"> <button @click="onClick1" ref="btn1">請(qǐng)求1</button> <button @click="onClick2" ref="btn2">請(qǐng)求2</button> </div> </body> <script> let sendPost = function(data){ return axios({ url: 'http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test', method: 'post', data }) } new Vue({ el: '#app', mounted() { this.$refs.btn1.click() this.$refs.btn2.click() }, methods: { // 使用lodash對(duì)請(qǐng)求方法做防抖 //這里有問(wèn)題,只是對(duì)每個(gè)按鈕的點(diǎn)擊事件單獨(dú)做了防抖,但是兩個(gè)按鈕之間做不到防抖的效果 onClick1: _.debounce(async function(){ let res = await sendPost({username:'zs', age: 20}) console.log('請(qǐng)求1的結(jié)果', res.data) }, 300), onClick2: _.debounce(async function(){ let res = await sendPost({username:'zs', age: 20}) console.log('請(qǐng)求2的結(jié)果', res.data) }, 300), }, }) </script> </html>
情況-在接口方法做防抖
按鈕間調(diào)用的方法是相同的,是可以對(duì)方法做防抖處理,但是處理本身對(duì)方法做了一次封裝,會(huì)影響到之前方法的返回值接收,需要對(duì)之前的方法做更多處理,變得更加復(fù)雜,不推薦
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/lodash.js/4.17.21/lodash.min.js" type="application/javascript"></script> <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js" type="application/javascript"></script> <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js" type="application/javascript"></script> </head> <body> <div id="app"> <button @click="onClick1" ref="btn1">請(qǐng)求1</button> <button @click="onClick2" ref="btn2">請(qǐng)求2</button> </div> </body> <script> // 使用lodash對(duì)請(qǐng)求方法做防抖, let sendPost = _.debounce(function(data){ //這里有問(wèn)題,這里的返回值不能作為sendPost方法執(zhí)行的返回值,因?yàn)閐ebounce內(nèi)部包裹了一層 return axios({ url: 'http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test', method: 'post', data }) }, 300) new Vue({ el: '#app', mounted() { this.$refs.btn1.click() this.$refs.btn2.click() }, methods: { onClick1: async function(){ //這里有問(wèn)題,sendPost返回值不是promise,而是undefined let res = await sendPost({username:'zs', age: 20}) console.log('請(qǐng)求1的結(jié)果', res) }, onClick2: async function(){ let res = await sendPost({username:'zs', age: 20}) console.log('請(qǐng)求2的結(jié)果', res) }, }, }) </script> </html>
方式2-通過(guò)取消ajax請(qǐng)求
(a) 概述
直接對(duì)請(qǐng)求方法做處理,通過(guò)ajax庫(kù)的api方法把重復(fù)的請(qǐng)求給取消掉
(b)原理
原生ajax取消請(qǐng)求
通過(guò)調(diào)用XMLHttpRequest
對(duì)象實(shí)例的abort
方法把請(qǐng)求給取消掉
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> </body> <script> //原生ajax的語(yǔ)法 let xhr = new XMLHttpRequest(); xhr.open("GET", "http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test?username=zs&age=20", true); xhr.onload = function(){ console.log(xhr.responseText) } xhr.send(); //在谷歌瀏覽器的低速3g下面測(cè)試 //通過(guò)XMLHttpRequest實(shí)例的abort方法取消請(qǐng)求 setTimeout(() => xhr.abort(), 100); </script> </html>
axios取消請(qǐng)求
通過(guò)axios
的CancelToken
對(duì)象實(shí)例cancel
方法把請(qǐng)求給取消掉
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js" type="application/javascript"></script> </head> <body> </body> <script> /*axios的取消的語(yǔ)法*/ // 方式1-通過(guò)axios.CancelToken.source產(chǎn)生cancelToken和cancel方法 /* const source = axios.CancelToken.source(); axios.get('http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test', { params: {username: 'zs', age: 20}, cancelToken: source.token }).then(res=>{ console.log('res', res.data) }).catch(err=>{ console.log('err', err) }) //在谷歌瀏覽器的低速3g下面測(cè)試 //通過(guò)調(diào)用source的cancel方法取消 setTimeout(() => source.cancel(), 100); */ /**/ // 方式2-通過(guò)new axios.CancelToken產(chǎn)生cancelToken和cancel方法 let cancelFn const cancelToken = new axios.CancelToken(cancel=>{ cancelFn = cancel }); axios.get('http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test', { params: {username: 'zs', age: 20}, cancelToken: cancelToken }).then(res=>{ console.log('res', res.data) }).catch(err=>{ console.log('err', err) }) //在谷歌瀏覽器的低速3g下面測(cè)試 //通過(guò)調(diào)用cancelFn方法取消 setTimeout(() => cancelFn(), 100); </script> </html>
(c)代碼
步驟1-通過(guò)axios請(qǐng)求攔截器取消重復(fù)請(qǐng)求
通過(guò)axios
請(qǐng)求攔截器,在每次請(qǐng)求前把請(qǐng)求信息和請(qǐng)求的取消方法放到一個(gè)map對(duì)象當(dāng)中,并且判斷map對(duì)象當(dāng)中是否已經(jīng)存在該請(qǐng)求信息的請(qǐng)求,如果存在取消上傳請(qǐng)求
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js" type="application/javascript"></script> <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js" type="application/javascript"></script> <script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/qs/6.10.3/qs.js" type="application/javascript"></script> </head> <body> <div id="app"> <button @click="onClick1" ref="btn1">請(qǐng)求1</button> <button @click="onClick2" ref="btn2">請(qǐng)求2</button> </div> </body> <script> //存儲(chǔ)請(qǐng)求信息和取消方法的的map對(duì)象 const pendingRequest = new Map(); //根據(jù)請(qǐng)求的信息(請(qǐng)求方式,url,請(qǐng)求get/post數(shù)據(jù)),產(chǎn)生map的key function getRequestKey(config){ const { method, url, params, data } = config; return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&"); } //請(qǐng)求攔截器 axios.interceptors.request.use( function (config) { //根據(jù)請(qǐng)求的信息(請(qǐng)求方式,url,請(qǐng)求get/post數(shù)據(jù)),產(chǎn)生map的key let requestKey = getRequestKey(config) //判斷請(qǐng)求是否重復(fù) if(pendingRequest.has(requestKey)){ //取消上次請(qǐng)求 let cancel = pendingRequest.get(requestKey) cancel() //刪除請(qǐng)求信息 pendingRequest.delete(requestKey) } //把請(qǐng)求信息,添加請(qǐng)求到map當(dāng)中 // 生成取消方法 config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => { // 把取消方法添加到map if (!pendingRequest.has(requestKey)) { pendingRequest.set(requestKey, cancel) } }) return config; }, (error) => { return Promise.reject(error); } ); let sendPost = function(data){ return axios({ url: 'http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test', method: 'post', data }) } new Vue({ el: '#app', mounted() { this.$refs.btn1.click() this.$refs.btn2.click() }, methods: { // 使用lodash對(duì)請(qǐng)求方法做防抖 //這里有問(wèn)題,只是對(duì)每個(gè)按鈕的點(diǎn)擊事件單獨(dú)做了防抖,但是兩個(gè)按鈕之間做不到防抖的效果 onClick1: async function(){ let res = await sendPost({username:'zs', age: 20}) console.log('請(qǐng)求1的結(jié)果', res.data) }, onClick2: async function(){ let res = await sendPost({username:'zs', age: 20}) console.log('請(qǐng)求2的結(jié)果', res.data) }, }, }) </script> </html>
步驟2-通過(guò)axios響應(yīng)攔截器處理請(qǐng)求成功
通過(guò)axios
的響應(yīng)攔截器,在請(qǐng)求成功后在map對(duì)象當(dāng)中,刪除該請(qǐng)求信息的數(shù)據(jù)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js" type="application/javascript"></script> <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js" type="application/javascript"></script> <script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/qs/6.10.3/qs.js" type="application/javascript"></script> </head> <body> <div id="app"> <button @click="onClick1" ref="btn1">請(qǐng)求1</button> <button @click="onClick2" ref="btn2">請(qǐng)求2</button> </div> </body> <script> //存儲(chǔ)請(qǐng)求信息和取消方法的的map對(duì)象 const pendingRequest = new Map(); //根據(jù)請(qǐng)求的信息(請(qǐng)求方式,url,請(qǐng)求get/post數(shù)據(jù)),產(chǎn)生map的key function getRequestKey(config){ const { method, url, params, data } = config; return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&"); } //請(qǐng)求攔截器 axios.interceptors.request.use( function (config) { //根據(jù)請(qǐng)求的信息(請(qǐng)求方式,url,請(qǐng)求get/post數(shù)據(jù)),產(chǎn)生map的key let requestKey = getRequestKey(config) //判斷請(qǐng)求是否重復(fù) if(pendingRequest.has(requestKey)){ //取消上次請(qǐng)求 let cancel = pendingRequest.get(requestKey) cancel() //刪除請(qǐng)求信息 pendingRequest.delete(requestKey) } //把請(qǐng)求信息,添加請(qǐng)求到map當(dāng)中 // 生成取消方法 config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => { // 把取消方法添加到map if (!pendingRequest.has(requestKey)) { pendingRequest.set(requestKey, cancel) } }) return config; }, (error) => { return Promise.reject(error); } ); //響應(yīng)攔截器 axios.interceptors.response.use( (response) => { //請(qǐng)求成功 //刪除請(qǐng)求的信息 let requestKey = getRequestKey(response.config) if(pendingRequest.has(requestKey)){ pendingRequest.delete(requestKey) } return response; }, (error) => { return Promise.reject(error); } ); let sendPost = function(data){ return axios({ url: 'http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test', method: 'post', data }) } new Vue({ el: '#app', mounted() { this.$refs.btn1.click() this.$refs.btn2.click() }, methods: { // 使用lodash對(duì)請(qǐng)求方法做防抖 //這里有問(wèn)題,只是對(duì)每個(gè)按鈕的點(diǎn)擊事件單獨(dú)做了防抖,但是兩個(gè)按鈕之間做不到防抖的效果 onClick1: async function(){ let res = await sendPost({username:'zs', age: 20}) console.log('請(qǐng)求1的結(jié)果', res.data) }, onClick2: async function(){ let res = await sendPost({username:'zs', age: 20}) console.log('請(qǐng)求2的結(jié)果', res.data) }, }, }) </script> </html>
步驟3-通過(guò)axios響應(yīng)攔截器處理請(qǐng)求失敗
通過(guò)axios
的響應(yīng)攔截器,在請(qǐng)求失敗后在map對(duì)象當(dāng)中,刪除該請(qǐng)求信息的數(shù)據(jù)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js" type="application/javascript"></script> <script src="https://lf9-cdn-tos.bytecdntp.com/cdn/expire-1-M/axios/0.26.0/axios.min.js" type="application/javascript"></script> <script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/qs/6.10.3/qs.js" type="application/javascript"></script> </head> <body> <div id="app"> <button @click="onClick1" ref="btn1">請(qǐng)求1</button> <button @click="onClick2" ref="btn2">請(qǐng)求2</button> </div> </body> <script> //存儲(chǔ)請(qǐng)求信息和取消方法的的map對(duì)象 const pendingRequest = new Map(); //根據(jù)請(qǐng)求的信息(請(qǐng)求方式,url,請(qǐng)求get/post數(shù)據(jù)),產(chǎn)生map的key function getRequestKey(config){ const { method, url, params, data } = config; return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&"); } //請(qǐng)求攔截器 axios.interceptors.request.use( function (config) { //根據(jù)請(qǐng)求的信息(請(qǐng)求方式,url,請(qǐng)求get/post數(shù)據(jù)),產(chǎn)生map的key let requestKey = getRequestKey(config) //判斷請(qǐng)求是否重復(fù) if(pendingRequest.has(requestKey)){ //取消上次請(qǐng)求 let cancel = pendingRequest.get(requestKey) cancel() //刪除請(qǐng)求信息 pendingRequest.delete(requestKey) } //把請(qǐng)求信息,添加請(qǐng)求到map當(dāng)中 // 生成取消方法 config.cancelToken = config.cancelToken || new axios.CancelToken(cancel => { // 把取消方法添加到map if (!pendingRequest.has(requestKey)) { pendingRequest.set(requestKey, cancel) } }) return config; }, (error) => { return Promise.reject(error); } ); //刪除請(qǐng)求信息 function delPendingRequest(config){ let requestKey = getRequestKey(config) if(pendingRequest.has(requestKey)){ pendingRequest.delete(requestKey) } } //響應(yīng)攔截器 axios.interceptors.response.use( (response) => { //請(qǐng)求成功 //刪除請(qǐng)求的信息 delPendingRequest(response.config) return response; }, (error) => { //請(qǐng)求失敗 //不是取消請(qǐng)求的錯(cuò)誤 if (!axios.isCancel(error)){ //服務(wù)器報(bào)400,500報(bào)錯(cuò),刪除請(qǐng)求信息 delPendingRequest(error.config || {}) } return Promise.reject(error); } ); let sendPost = function(data){ return axios({ url: 'http://nodejs-cloud-studio-demo-bkzxs.nodejs-cloud-studio-demo.50185620.cn-hangzhou.fc.devsapp.net/test', method: 'post', data }) } new Vue({ el: '#app', mounted() { this.$refs.btn1.click() this.$refs.btn2.click() }, methods: { // 使用lodash對(duì)請(qǐng)求方法做防抖 //這里有問(wèn)題,只是對(duì)每個(gè)按鈕的點(diǎn)擊事件單獨(dú)做了防抖,但是兩個(gè)按鈕之間做不到防抖的效果 onClick1: async function(){ let res = await sendPost({username:'zs', age: 20}) console.log('請(qǐng)求1的結(jié)果', res.data) }, onClick2: async function(){ let res = await sendPost({username:'zs', age: 20}) console.log('請(qǐng)求2的結(jié)果', res.data) }, }, }) </script> </html>
以上就是vue阻止重復(fù)請(qǐng)求實(shí)現(xiàn)示例詳解的詳細(xì)內(nèi)容,更多關(guān)于vue阻止重復(fù)請(qǐng)求的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue+element_ui上傳文件,并傳遞額外參數(shù)操作
這篇文章主要介紹了vue+element_ui上傳文件,并傳遞額外參數(shù)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12圖文詳解Element-UI中自定義修改el-table樣式
elementUI提供的組件間距、樣式都比較大,如果直接套用,在頁(yè)面顯示可能就會(huì)顯得很大,就比如表格,表頭、行寬如果不修改的話(huà),遇到列較多的時(shí)候,會(huì)顯得整個(gè)頁(yè)面就不好看,下面這篇文章主要給大家介紹了關(guān)于Element-UI中自定義修改el-table樣式的相關(guān)資料,需要的朋友可以參考下2022-08-08keep-alive include和exclude無(wú)效問(wèn)題及解決
這篇文章主要介紹了keep-alive include和exclude無(wú)效問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11Vue動(dòng)態(tài)組件和keep-alive組件實(shí)例詳解
動(dòng)態(tài)組件指的是動(dòng)態(tài)切換組件的顯示與隱藏,下面這篇文章主要給大家介紹了關(guān)于Vue動(dòng)態(tài)組件和keep-alive組件的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05用vscode開(kāi)發(fā)vue應(yīng)用的方法步驟
這篇文章主要介紹了用vscode開(kāi)發(fā)vue應(yīng)用的方法步驟,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-05-05