前言
在一些項目里,我們可能有著大量的下拉框,而這些下拉框的數(shù)據(jù)就來源于我們后端接口返回的字典信息。于是,畫風(fēng)可能是這樣的,每次下拉,你都需要請求一次字典接口拿到這些數(shù)據(jù),于是每次組件刷新都會重復(fù)請求接口,造成性能上的浪費,如下圖所示:
那么,我們可不可以把這些字典值緩存起來,只在第一次加載時請求一次呢?
使用vuex緩存字典數(shù)據(jù)
在Vue
項目中,我們要實現(xiàn)數(shù)據(jù)的緩存,自然要考慮到Vuex
、Pinia
狀態(tài)管理了。
思路是這樣的:每個字典肯定有一個key,也就是其名字,每次拿字典值先從狀態(tài)管理中尋找字典的key,如果找不到或者數(shù)據(jù)為空,我們就請求接口,響應(yīng)后先把數(shù)據(jù)處理后存放在狀態(tài)管理后再返回;當?shù)诙握埱髸r,自然地就從狀態(tài)管理中取值而不是再次請求接口了。
新建測試項目,后臺使用koa
返回簡單的字典值:
前端為Vue3
+Vuex
,使用Element-Plus
組件庫中的Select
下拉組件,此下拉組件需要的數(shù)據(jù)格式如下:
[{label:"",value:""}]
Vuex
中相關(guān)代碼為:
const store =createStore({
state:{
dicData:{
}
},
mutations:{
getDic(state:any,key:string){
return state.dicData[key]
},
setDic(state:any,data:{key:string;list:any[]}){
const {key,list} =data;
state.dicData[key]=list
}
},
actions:{
async GET_DIC(context:any,payload:any){
const {key} = payload
if(context.state.dicData[key]){
return context.state.dicData[key]
} else{
const data =(await getDicData(key)).data
const list = data.map(item=>({
value: item.key || item.code,
label: item.value || item.name
}))
context.commit('setDic',{key:key,list:list});
return list
}
}
}
})
?文章來源地址http://www.zghlxwxcb.cn/news/detail-632160.html
在actions的GET_DIC
方法中,我們根據(jù)傳入的key先從state中的dicData
取出字典值,取不到后才請求接口getDicData
拿到值,做數(shù)據(jù)處理后,先通過mutation的setDic
方法設(shè)置字典數(shù)據(jù)再返回。
調(diào)用如下:
store.dispatch("GET_DIC",{key:"fruit"}).then(res=>{
options.value=res;
})
這樣,無論組件怎么刷新接口始終只調(diào)用一次,節(jié)省了等待時間和帶寬。
如果后端接口沒有這么規(guī)范,每一個字典都單獨做了一個接口,也可以利用swtich
,根據(jù)不同的key單獨寫處理數(shù)據(jù)的邏輯,最終在dicData
的值均是形如以下的數(shù)據(jù)格式就好了。
dicData:{
"fruit":[{label:"蘋果",value:101}],
"class":[{label:"一年級一班",value:201}]
}
Pinia的實現(xiàn)
export const useSelectStore =defineStore('select',{
state:()=>{
return{
dicData:{}
}
},
actions:{
setDic(key:string,list:any[]){
this.dicData[key]=list;
},
async getDic(key:string){
console.log(this.dicData)
if(this.dicData[key]){
return this.dicData[key]
} else{
const data =(await getDicData(key)).data;
const list = data.map(item=>({
value: item.key || item.code,
label: item.value || item.name
}));
this.setDic(key,list);
return list;
}
}
}
})
調(diào)用
const selectStore= useSelectStore();
selectStore.getDic(props.requestKey).then(res=>{
options.value=res;
})
Pinia
代碼上差不了太多,不過其本身的確比Vuex
簡潔一些。
同時渲染造成的請求并發(fā)問題
評論區(qū)有小伙伴說到了萬一同時渲染會發(fā)起多個一樣的請求,也就是請求并發(fā)的問題。如圖所示,頁面有四個班級列表的下拉。渲染該頁面則每個下拉都會發(fā)起一個請求,即:
可以看到,班級的字典數(shù)據(jù)請求共有4次,違背了我們設(shè)計的初衷。那么,我們?nèi)绾谓鉀Q呢?就要對接口本身做緩存了,而這個緩存方案也很簡單,我們這塊只需要對字典數(shù)據(jù)的接口單獨緩存,使用Promise
就夠了。
先上代碼(以Pinia為例):
const cacheMap={};
?
export const useSelectStore =defineStore('select',{
? ?state:()=>{
? ? ? ?return{
? ? ? ? ? ?dicData:{}
? ? ? }
? },
? ?actions:{
? ? ? ?setDic(key:string,list:any[]){
? ? ? ? ? ?this.dicData[key]=list;
? ? ? },
? ? ? getDic(key:string){
? ? ? ? ? ?// console.log(this.dicData)
? ? ? ? ? ?if(this.dicData[key]){
? ? ? ? ? ? ? ?return new Promise((resolve,reject)=>{
? ? ? ? ? ? ? ? ? ?resolve(this.dicData[key])
? ? ? ? ? ? ? })
? ? ? ? ? } else{
? ? ? ? ? ? ? ?// return new Promise((resolve,reject)=>{
? ? ? ? ? ? ? ?// ? ? getDicData(key).then(res=>{
? ? ? ? ? ? ? ?// ? ? ? ? const data =res.data;
? ? ? ? ? ? ? ?// ? ? ? ? const list = data.map(item=>({
? ? ? ? ? ? ? ?// ? ? ? ? ? ? value: item.key || item.code,
? ? ? ? ? ? ? ?// ? ? ? ? ? ? label: item.value || item.name
? ? ? ? ? ? ? ?// ? ? ? ? }));
? ? ? ? ? ? ? ?// ? ? ? ? this.setDic(key,list);
? ? ? ? ? ? ? ?// ? ? ? ? resolve(list);
? ? ? ? ? ? ? ?// ? ? })
? ? ? ? ? ? ? ?// })
? ? ? ? ? ? ? ?//添加接口并發(fā)緩存處理
? ? ? ? ? ? ? ?if((cacheMap?.[key]?.length ?? 0)==0){
? ? ? ? ? ? ? ? ? ?cacheMap[key]=[];
? ? ? ? ? ? ? ? ? ?getDicData(key).then(res=>{
? ? ? ? ? ? ? ? ? ? ? ?const data =res.data;
? ? ? ? ? ? ? ? ? ? ? ?while(cacheMap[key].length){
? ? ? ? ? ? ? ? ? ? ? ? ? ?const list = data.map(item=>({
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?value: item.key || item.code,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?label: item.value || item.name
? ? ? ? ? ? ? ? ? ? ? ? ? }));
? ? ? ? ? ? ? ? ? ? ? ? ? ?this.setDic(key,list);
? ? ? ? ? ? ? ? ? ? ? ? ? ?const resolve = cacheMap[key].shift();
? ? ? ? ? ? ? ? ? ? ? ? ? ?resolve[0](list)
? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? })
? ? ? ? ? ? ? }
? ? ? ? ? ? ? ?return new Promise((resolve,reject)=>{
? ? ? ? ? ? ? ? ? ?cacheMap[key].push([resolve,reject]);
? ? ? ? ? ? ? })
? ? ? ? ? }
? ? ? }
? }
})
?
?
cacheMap
為全局的緩存對象,其每個屬性key
為字典的key,每次請求返回一個promise
,并將resolve
和reject
回調(diào)存儲下來。對于cacheMap[key]
,其數(shù)組為空時,可知是首次請求,此時調(diào)用接口獲取數(shù)據(jù)。由于接口請求也是異步,實例化Promise
的語句是同步的,當請求響應(yīng)前所有的resolve
與reject
回調(diào)已經(jīng)緩存。我們調(diào)用所有的resolve
完成所有的promise
并清空數(shù)組,就實現(xiàn)了簡單的接口緩存。
文章來源:http://www.zghlxwxcb.cn/news/detail-632160.html
?
到了這里,關(guān)于Vue緩存字典值減少網(wǎng)絡(luò)請求次數(shù),解決同樣參數(shù)并發(fā)請求多次的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!