前端做后台管控系統,在某些接口請求時間過長的場景下,需要防止用戶反復發起請求。
假設某場景下用戶點擊查詢按鈕后,后端響應需要長時間才能返回數據。那么要規避用戶返回點擊查詢按鈕無外乎是讓用戶無法在合理時間內再次點擊按鈕。實現方式也有好幾種:
1、在按鈕點擊發起請求后,彈個蒙層,顯示個loading,等請求數據返回了將蒙層隱藏掉。
2、在按鈕點擊發起請求后,將按鈕禁用掉,同樣等數據返回了將按鈕禁用解除。
以上是比較常見的2種方案。
實現上最簡單的肯定是在需要的頁面種在請求前和拿到數據后,單獨處理。這種方案優點僅僅是簡單,但是每個需要處理的頁面都要單獨寫一串重復的代碼,哪怕利用mixin也要多不少冗余代碼。
如果是利用指令的方式僅僅需要在合適的地方加上個一條v-xxxx,其他都在指令的邏輯內統一處理。
以第二種方式為例:
clickForbidden.js
let forbidClick = null;
export default {
bind(e) {
const el = e;
let timer = null;
forbidClick = () => {
el.disabled = true;
el.classList.add('is-disabled');
timer = setTimeout(() => {
el.disabled = false;
el.classList.remove('is-disabled');
}, 3000);
};
el.addEventListener('click', forbidClick);
},
unbind() {
document.removeEventListener('click', forbidClick);
},
};
指令的邏輯很簡單,當按鈕插入到DOM節點后,添加一個監聽click的事件,當按鈕點擊后,就將按鈕禁用,並加上一個禁用樣式,並在3s后將該按鈕解除禁用。
再考慮請求,以axios為例:
api.js
import axios from 'axios';
export baseURL = 'xxxx';
const api = axios.create({
baseURL,
timeout: 3000,
});
/* 記錄當前請求是否完成 */
window.currentResq = {
done: true,
config: {},
};
api.interceptors.request.use(function(config) {
clearTimeout(resqTimer);
window.currentResq = {
done: false,
config,
};
// 接口請求時長超過3s,則視為完成,不管請求結果成功或失敗
resqTimer = setTimeout(() => {
window.currentResq = {
done: true,
config: {},
};
}, 3000);
});
api.interceptors.response.use(function(response) {
const { config } = window.currentResq;
const { url, method, data } = response.config;
if (config.url === url && config.method === method && config.data === data) {
clearTimeout(resqTimer);
window.currentResq.done = true;
}
return response;
}, function (error) {
return error;
});
export default api;
用一個全局的currentResq來作為請求是否完成的標志。在axios請求攔截器種,將當前請求的數據記錄在currentResq中,並將done設置為false。在axios響應攔截器中,約定url,method,data3個參數一樣時,就是當前currentResq中記錄的請求返回數據,並將done設置為true。
同樣的在指令邏輯中加入一個輪詢監聽currentResq的done是否完成。
clickForbidden.js
let forbidClick = null;
export default {
bind(e) {
const el = e;
let timer = null;
forbidClick = () => {
el.disabled = true;
el.classList.add('is-disabled');
timer = setInterval(() => {
if (window.currentResq.done) {
clearInterval(timer);
el.disabled = false;
el.classList.remove('is-disabled');
}
}, 500);
};
el.addEventListener('click', forbidClick);
},
unbind() {
document.removeEventListener('click', forbidClick);
},
};
這樣就實現了只要在按鈕上加上了v-clickForbidden。按鈕點擊后就會被禁用,僅當某個請求返回數據或者3s后將按鈕的禁用解除。
現在僅僅考慮按鈕一次僅發送了一個請求的場景,在currentResq中也可以用一個數據來記錄請求。
