前端解决重复调用接口

263 阅读2分钟
  1. 背景:项目页面使用的组件会自调用请求,同一个页面重复使用了很多这个组件,导致发送了大量相同的请求。
  2. 解决思路:缓存相同的接口请求结果。遇到相同接口,相同参数的请求,就从缓存直接拿结果。
  3. 代码实现
import _ from 'lodash-es';  
  
/**  
* 缓存接口数据  
*/  
class CacheAPI {  
    map = new Map();  

    // 防止和参数冲突  
    symbolApi = Symbol('api');  

    /**  
    * 组合KEY  
    * @param api  
    * @param param  
    * @returns {{symbolApi: string}}  
    */  
    // eslint-disable-next-line class-methods-use-this  
    createKey(api = '', param = {}) {  
        return { [this.symbolApi]: api, ...param };  
    }  

    /**  
    * 发起请求,请求中,命中请求结果  
    * @param api  
    * @param param  
    * @param axioxFn  
    * @param resolve  
    */  
    // eslint-disable-next-line default-param-last  
    sendHttpRequst(api = '', param = {}, axioxFn, resolve) {  
        const KEY = this.createKey(api, param);  
        // console.log('>>> KEY', KEY, this.map);  
        // window.__map = this.map;  
        // 有缓存  
        if (this.hasApiRequst(KEY)) {  
            const value = this.getMapValueReslut(KEY);  
            value.hotNum += 1;  
            if (value.reslut) {  
                resolve(value.reslut);  
                this.checkOutMapMaxToRemove();  
            } else {  
                value.promis.push(resolve);  
            }  
         } else {  
            this.initApi(KEY, resolve);  
            axioxFn().then((res) => {  
                this.addResult(KEY, res);  
            });  
        }  
    }  

    /**  
    * 添加响应结果,并且返回所有完全相同接口的数据  
    * @param KEY  
    * @param reslut  
    */  
    addResult(KEY, reslut) {  
        for (const item of this.map) {  
        if (_.isEqual(item[0], KEY)) {  
            const value = item[1];  
            value.reslut = reslut;  
            value.promis.forEach((resolv) => {  
                resolv(value.reslut);  
            });  
            value.promis = [];  
            }  
        }  
    }  

    /**  
    * 是否有发起过一样的请求【可能在请求中】  
    * @param KEY  
    * @returns {boolean}  
    */  
    hasApiRequst(KEY) {  
        let flag = false;  
        for (const item of this.map) {  
            if (_.isEqual(item[0], KEY)) {  
                flag = true;  
            }  
        }  
        return flag;  
    }  

    /**  
    * 获取某个命中的请求map项  
    * @param api  
    * @param param  
    */  
    getMapValueReslut(KEY) {  
        let value = {};  
        for (const item of this.map) {  
            if (_.isEqual(item[0], KEY)) {  
                value = item[1];  
            }  
        }  
     return value;  
    }  

    /**  
    * 清空 Map  
    * 切换路由调用  
    */  
    clearMap() {  
        this.map.clear();  
    }  

    /**  
    * 超过设置缓存最大数,就清除热度较低的缓存  
    * 最大100个  
    */  
    checkOutMapMaxToRemove() {  
        if (this.map.size > 100) { 
            for (const item of this.map) {  
                if (item[1].hotNum === 1) {  
                    this.map.delete(item[0]);  
                }  
            }  
        }  
    }  

    /**  
    * 创建无相关KEY的请求数据结构  
    * @param key  
    * @param resolve  
    */  
    // eslint-disable-next-line class-methods-use-this  
    initApi(KEY, resolve) {  
        const value = {  
            reslut: null, // api返回的结果  
            promis: [], // resove 集合  
            hotNum: 1, // 热度  
        };  
        value.promis.push(resolve);  
        this.map.set(KEY, value);  
    }  
    
}  
export default new CacheAPI();
  1. 使用
import cacheAPI from '@/util/CacheAPI.js';

export const getUserList = (params = {}) => {  
    const API = '/xxxx/api';  
    return new Promise((resolve) => {  
        const axioxFn = () =>  
            request({  
            url: API,  
            method: 'get',  
            params: {  
            ...params,  
            },  
        });  
        cacheAPI.sendHttpRequst(API, params, axioxFn, resolve);  
    });  
};
  1. 在合适的时机要调用一下 clearMap,防止内存爆了