1 解决什么问题
项目中存在大量重复接口,请求地址,方法,参数一模一样,该怎么样优化呢?同一个接口被调了七八次!
梳理一下整体优化逻辑:
-
创建缓存对象;
-
请求发起之前判断该请求是否命中缓存:
- 是,直接返回缓存内容;
- 否,发起请求,请求成功后将请求结果存入缓存中。
2 LRU算法介绍
什么是LRU算法
-
Least Recently Used 淘汰算法以时间作为参考,淘汰最长时间未被使用的数据
-
如果数据最近被访问过,那么将来被访问的几率也更高;会淘汰最长时间没有被使用的元素(都没人要你了,不淘汰你淘汰谁)
-
-
基本原理是:在缓存满时,将最近最久未使用的数据淘汰出缓存,以便给新的数据留出空间。
-
实现方式可以用:数组、链表等方式
-
- 新插入的数据放在头部,最近访问过的也移到头部,空间满时将尾部元素删除
3 实现流程
先创建LRUCache类,构造函数中capacity表示该缓存数组最多可以缓存多少元素, 包括两个核心方法,get获取缓存列表中的key对应的value,如果没有返回-1,set方法用于向缓存列表中添加元素。
如果缓存容量已满,删除最近最少使用的缓存
export default class LRUCache {
constructor (capacity) {
if (typeof capacity !== 'number' || capacity < 0) {
throw new TypeError('capacity必须是一个非负数')
}
this.capacity = capacity
this.cache = new Map()
}
get (key) {
if (!this.cache.has(key)) {
return -1
}
let tmp = this.cache.get(key)
// 将当前的缓存移动到最常用的位置
this.cache.delete(key)
this.cache.set(key, tmp)
return tmp
}
set (key, value) {
if (this.cache.has(key)) {
// 如果缓存存在更新缓存位置
this.cache.delete(key)
} else if (this.cache.size >= this.capacity) {
// 如果缓存容量已满,删除最近最少使用的缓存
this.cache.delete(this.cache.keys().next.val)
}
this.cache.set(key, value)
}
}
4结合Axios实现请求缓存
理一下大概的逻辑:每次请求根据请求的方法、url、参数生成一串hash,缓存内容为hash->response,后续请求如果请求方法、url、参数一致,即认为命中缓存。
代码如下:
import axios from 'axios';
import md5 from 'md5';
import LRUCache from './LRU.js';
const cache = new LRUCache(100);
const _axios = axios.create();
// 将请求参数排序,防止相同参数生成的hash不同
function sortObject(obj = {}) {
let result = {};
Object.keys(obj)
.sort()
.forEach((key) => {
result[key] = obj[key];
});
}
// 根据request method,url,data/params生成cache的标识
function genHashByConfig(config) {
const target = {
method: config.method,
url: config.url,
params: config.method === 'get' ? sortObject(config.params) : null,
data: config.method === 'post' ? sortObject(config.data) : null,
};
return md5(JSON.stringify(target));
}
_axios.interceptors.response.use(
function(response) {
// 设置缓存
const hashKey = genHashByConfig(response.config);
cache.set(hashKey, response.data);
return response.data;
},
function(error) {
return Promise.reject(error);
}
);
// 将axios请求封装,如果命中缓存就不需要发起http请求,直接返回缓存内容
export default function request({
method,
url,
params = null,
data = null,
...res
}) {
const hashKey = genHashByConfig({ method, url, params, data });
const result = cache.get(hashKey);
if (~result) {
console.log('cache hit');
return Promise.resolve(result);
}
return _axios({ method, url, params, data, ...res });
}
请求的封装:
import request from './axios.js';
export function getApi(params) {
return request({
method: 'get',
url: '/list',
params,
});
}
export function postApi(data) {
return request({
method: 'post',
url: '/list',
data,
});
}
5 项目效果
原项目加载首页请求了59个接口,优化后请求了30个接口
,将近30个重复请求的接口