幾行代碼,優(yōu)雅的避免接口重復請求!同事都說好!
共 8441字,需瀏覽 17分鐘
·
2024-08-01 09:15
點擊上方 前端Q,關(guān)注公眾號
回復加群,加入前端Q技術(shù)交流群
背景簡介
我們?nèi)粘i_發(fā)中,經(jīng)常會遇到點擊一個「按鈕」或者進行「搜索」時,請求接口的需求。
如果我們不做優(yōu)化,連續(xù)點擊「按鈕」或者進行「搜索」,接口會重復請求。
?首先,這會導致性能浪費!最重要的,如果接口響應比較慢,此時,我們在做其他操作會有一系列bug!
?
那么,我們該如何規(guī)避這種問題呢?
如何避免接口重復請求
防抖節(jié)流方式(不推薦)
使用防抖節(jié)流方式避免重復操作是前端的老傳統(tǒng)了,不多介紹了
防抖實現(xiàn)
<template>
<div>
<button @click="debouncedFetchData">請求</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import axios from 'axios';
const timeoutId = ref(null);
function debounce(fn, delay) {
return function(...args) {
if (timeoutId.value) clearTimeout(timeoutId.value);
timeoutId.value = setTimeout(() => {
fn(...args);
}, delay);
};
}
function fetchData() {
axios.get('http://api/gcshi) // 使用示例API
.then(response => {
console.log(response.data);
})
}
const debouncedFetchData = debounce(fetchData, 300);
</script>
「防抖(Debounce)」 :
-
在setup函數(shù)中,定義了timeoutId用于存儲定時器ID。 -
debounce函數(shù)創(chuàng)建了一個閉包,清除之前的定時器并設(shè)置新的定時器,只有在延遲時間內(nèi)沒有新調(diào)用時才執(zhí)行fetchData。 -
debouncedFetchData是防抖后的函數(shù),在按鈕點擊時調(diào)用。
節(jié)流實現(xiàn)
<template>
<div>
<button @click="throttledFetchData">請求</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import axios from 'axios';
const lastCall = ref(0);
function throttle(fn, delay) {
return function(...args) {
const now = new Date().getTime();
if (now - lastCall.value < delay) return;
lastCall.value = now;
fn(...args);
};
}
function fetchData() {
axios.get('http://api/gcshi') //
.then(response => {
console.log(response.data);
})
}
const throttledFetchData = throttle(fetchData, 1000);
</script>
「節(jié)流(Throttle)」 :
-
在setup函數(shù)中,定義了lastCall用于存儲上次調(diào)用的時間戳。 -
throttle函數(shù)創(chuàng)建了一個閉包,檢查當前時間與上次調(diào)用時間的差值,只有大于設(shè)定的延遲時間時才執(zhí)行fetchData。 -
throttledFetchData是節(jié)流后的函數(shù),在按鈕點擊時調(diào)用。
節(jié)流防抖這種方式感覺用在這里不是很絲滑,代碼成本也比較高,因此,很不推薦!
請求鎖定(加laoding狀態(tài))
請求鎖定非常好理解,設(shè)置一個laoding狀態(tài),如果第一個接口處于laoding中,那么,我們不執(zhí)行任何邏輯!
<template>
<div>
<button @click="fetchData">請求</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import axios from 'axios';
const laoding = ref(false);
function fetchData() {
// 接口請求中,直接返回,避免重復請求
if(laoding.value) return
laoding.value = true
axios.get('http://api/gcshi') //
.then(response => {
laoding.value = fasle
})
}
const throttledFetchData = throttle(fetchData, 1000);
</script>
這種方式簡單粗暴,十分好用!
「但是也有弊端,比如我搜索A后,接口請求中;但我此時突然想搜B,就不會生效了,因為請求A還沒響應」!
因此,請求鎖定這種方式無法取消原先的請求,只能等待一個請求執(zhí)行完才能繼續(xù)請求。
axios.CancelToken取消重復請求
基本用法
axios其實內(nèi)置了一個取消重復請求的方法:axios.CancelToken,我們可以利用axios.CancelToken來取消重復的請求,爆好用!
首先,我們要知道,aixos有一個config的配置項,取消請求就是在這里面配置的。
<template>
<div>
<button @click="fetchData">請求</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import axios from 'axios';
let cancelTokenSource = null;
function fetchData() {
if (cancelTokenSource) {
cancelTokenSource.cancel('取消上次請求');
cancelTokenSource = null;
}
cancelTokenSource = axios.CancelToken.source();
axios.get('http://api/gcshi',{cancelToken: cancelTokenSource.token}) //
.then(response => {
laoding.value = fasle
})
}
</script>
我們測試下,如下圖:可以看到,重復的請求會直接被終止掉!
CancelToken官網(wǎng)示例
?官網(wǎng)使用方法傳送門:https://www.axios-http.cn/docs/cancellation
?
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 處理錯誤
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// 取消請求(message 參數(shù)是可選的)
source.cancel('Operation canceled by the user.');
也可以通過傳遞一個 executor 函數(shù)到 CancelToken 的構(gòu)造函數(shù)來創(chuàng)建一個 cancel token:
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// executor 函數(shù)接收一個 cancel 函數(shù)作為參數(shù)
cancel = c;
})
});
// 取消請求
cancel();
注意: 可以使用同一個 cancel token 或 signal 取消多個請求。
在過渡期間,您可以使用這兩種取消 API,即使是針對同一個請求:
const controller = new AbortController();
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token,
signal: controller.signal
}).catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 處理錯誤
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// 取消請求 (message 參數(shù)是可選的)
source.cancel('Operation canceled by the user.');
// 或
controller.abort(); // 不支持 message 參數(shù)
往期推薦
最后
歡迎加我微信,拉你進技術(shù)群,長期交流學習...
歡迎關(guān)注「前端Q」,認真學前端,做個專業(yè)的技術(shù)人...
