字典组件接口优化
背景
在很多的PC端管理项目中,总会有很多字典的接口,对于重复字典接口,如果每一次都请求,会造成非必要的性能丢失。
思路
对于该痛点,我们能很快的想到复用接口请求的数据就好。而且一般像字典请求接口来说,请求参数简单固定化。对于这些接口,能实现map存储复用结果。
由于axios请求是个promise,只要第一次请求之后,将该promise存储起来,后续重复接口调用时都复用该接口。(promise的特性,状态一旦修改之后就不会被二次修改,永远都保持用一个值)
设计
javascript
/**
* 存放dict缓存的字段
* 目前只针对页面级缓存,切换页面dictCacheMap会清空
*/
const dictCacheMap = new Map();
/**
* 字典请求接口
*/
const dictUrl = [
'dictDetail/list', // 获取字典,params: { dictName: String }
'dictDetail/treeList', // 获取字典项树, parmas: { dictName: String }
];
/**
* 判断是否命中缓存接口
* @param {URL} url
* @returns Boolean
*/
export const isDictUrl = url => {
return dictUrl.some(dict => url.endsWith(dict));
};
/**
* 判断是否已经缓存过字典
* @param {String} dictName 请求的参数
* @returns Boolean
*/
export const checkDictCache = dictName => {
return dictCacheMap.has(dictName);
};
/**
* 返回字典值
* @param {String} dictName 请求的参数
* @returns Promise
*/
export const getDictCache = dictName => {
return dictCacheMap.get(dictName);
};
/**
* 设置字典值
* @param {String} dictName 请求的参数
* @param {Promise} promise 请求promise
* @returns Promise
*/
export const setDictCache = (dictName, promise) => {
// 将请求的promise收集起来,重复的接口将该promise返回
dictCacheMap.set(dictName, promise);
return promise;
};
/**
* 清除字典
* @param {...any} rest 清除的字典名,不输入默认清除全部
*/
export const clearDictCache = (...rest) => {
if (rest.length === 0) {
dictCacheMap.clear();
} else {
for (const v of rest) {
dictCacheMap.delete(v);
}
}
};
在定义好数据存储的文件后,我们可对字典请求接口封装成组件,或是请求request拦截请求设置。在这里采用组件形式书写。
xml
<!--
字典选择
-->
<template>
<a-select
v-listening="$listening"
v-bind="$attrs"
:value="dtValue"
:options="dictItems"
/>
</template>
<script>
import {
checkDictCache,
getDictCache,
setDictCache,
} from './dict-cache';
export default {
props: {
value: null, // 值
dict: {
type: String,
default: ''
}
},
data() {
return {
dtValue: this.value,
dictItems: [], // 字典项
url: {
list: 'dict/list', // 平铺的字典项, params: { dictName: String }
},
};
},
computed: {
// 请求配置
requestOption() {
const option = {
method: 'post',
params: {
dictName: this.dict, // 请求的字典名
},
};
return option;
},
},
watch: {
value(v) {
this.dtValue = v;
},
// 请求配置变化后,触发一次接口调用
requestOption() {
this.fetchList();
},
},
mounted() {
this.fetchList();
},
methods: {
fetchList() {
// 是否命中字典缓存接口
const fetchPromise = checkDictCache(this.dict)
// 如果已经存在请求,复用之前请求的结果
? getDictCache(this.dict)
// 如果不存在该请求,发送请求,并将请求promise复用起来
: setDictCache(this.dict, this.fetchPromise());
fetchPromise.then(res => {
this.dictItems = res;
});
},
// 发送请求
fetchPromise() {
return this.$$http(this.requestOption)
.then(data => {
return data;
});
},
},
};
</script>
对于字典请求接口缓存后,需要考虑删除缓存结果,一般来说我们针对页面级的缓存,当这个页面一直存在时,缓存就不会被清除。因此,清除缓存可以在路由的beforeEach钩子函数执行时主动清除缓存
javascript
import { clearDictCache } from './dict-cache.js'
const router = createRouter({ ... })
router.beforeEach((to, from) => {
clearDictCache()
//
})
总结
接口缓存一种很常见的问题,很多方式都是采用map存储请求回来的 结果。但是在这种方式下会导致第一次数据还没请求到,到后续请求又需要该结果。因此,最好使用promise来兜底。promise对处理异步有着很多的优势!