• 利用promise和装饰器封装一个缓存api请求的装饰器工具


    实际情景

    在网页实际运行当中存在很多接口的数据在一定时间都不会变动,然而却要经常请求,这样就会带来后台服务器的巨大压力,也为前端数据请求的效率带来很大的影响。像这种数据不会变动的接口占用了太多带宽,因此这些接口的数据最好缓存起来。在前端 api 请求缓存方案的基础上结合自己的实际需求所封装的一个缓存api请求的装饰器工具。

    封装的工具

    封装装饰器(JS 装饰器,一篇就够

    // 制定修饰器
    export default function ApiCache(...args) {
      return decorate(handleApiCache, args);
    }
    
    function decorate(handleDescription: any, entryArgs: any) {
      // 判断当前最后数据是否是descriptor,如果是descriptor,直接使用
      // 例如 log 这样的修饰器
      if (isDescriptor(entryArgs[entryArgs.length - 1])) {
        return handleDescription(...entryArgs);
      } else {
        // 如果不是
        // 例如 add(1) plus(20) 这样的修饰器
        return function() {
          return handleDescription(...Array.prototype.slice.call(arguments), ...entryArgs);
        };
      }
    }
    
    function isDescriptor(descriptor: any) {
      if (descriptor) {
        return (
          descriptor.hasOwnProperty('configurable') &&
          descriptor.hasOwnProperty('enumerable') &&
          descriptor.hasOwnProperty('writable') &&
          descriptor.hasOwnProperty('value')
        );
      }
      return false;
    }
    
    function handleApiCache(target: any, name: any, descriptor: any, ...config: any) {
      // 拿到函数体并保存
      const fn = descriptor.value;
      // 修改函数体
      descriptor.value = function() {
        const key = generateKey(name, arguments[0]);
        let promise = ExpiresCache.get(key);
        if (!promise) {
          // 设定promise
          promise = fn.apply(null, arguments).catch((error: any) => {
            // 在请求回来后,如果出现问题,把promise从cache中删除
            ExpiresCache.delete(key);
            // 返回错误
            return Promise.reject(error);
          });
          // 使用缓存,缓存过期之后再次get就会获取null,而从服务端继续请求
          ExpiresCache.set(key, promise, config[0]);
          // console.log(ExpiresCache.cacheMap);
        }
        return promise;
      };
      return descriptor;
    }
    
    // 生成key值
    function generateKey(name: string, config: any) {
      return encodeURIComponent(`${name}-${JSON.stringify(config.data)}`);
    }
    
    class ItemCache {
      private data: any;
      private timeout: number;
      private cacheTime: number;
    
      constructor(data: any, timeout: number) {
        this.data = data;
        // 设定超时时间,设定为多少秒
        this.timeout = timeout;
        // 创建对象时候的时间,大约设定为数据获得的时间
        this.cacheTime = new Date().getTime();
      }
    }
    
    class ExpiresCache {
      // 定义静态数据map来作为缓存池
      static cacheMap = new Map();
    
      // 数据是否超时
      static isOverTime(name: any) {
        const data = ExpiresCache.cacheMap.get(name);
    
        // 没有数据 一定超时
        if (!data) {
          return true;
        }
    
        // 获取系统当前时间戳
        const currentTime = new Date().getTime();
    
        // 获取当前时间与存储时间的过去的秒数
        const overTime = (currentTime - data.cacheTime) / 1000;
    
        // 如果过去的秒数大于当前的超时时间,也返回null让其去服务端取数据
        if (Math.abs(overTime) > data.timeout) {
          // 此代码可以没有,不会出现问题,但是如果有此代码,再次进入该方法就可以减少判断。
          ExpiresCache.cacheMap.delete(name);
          return true;
        }
    
        // 不超时
        return false;
      }
    
      // 删除 cache 中的 data
      static delete(name: string | Error) {
        return ExpiresCache.cacheMap.delete(name);
      }
    
      // 获取
      static get(name: string | Error) {
        const isDataOverTime = ExpiresCache.isOverTime(name);
        // 如果 数据超时,返回null,但是没有超时,返回数据,而不是 ItemCache 对象
        return isDataOverTime ? null : ExpiresCache.cacheMap.get(name).data;
      }
    
      // 默认存储60分钟
      static set(name: string | Error, data: any, timeout = 3600) {
        // 设置 itemCache
        const itemCache = new ItemCache(data, timeout);
        // 缓存
        ExpiresCache.cacheMap.set(name, itemCache);
      }
    }

    给api请求添加上装饰器

    import Api from '@/utils/request';
    import ApiCache from '@/utils/apiCache';
    
    // 因为装饰器只能装饰类和类里面的方法,因此要把api请求放到类当中 class Common { @ApiCache tree(params
    = {}) { return Api.tree(params); } } export const common = new Common();

    页面当中实际调用

    import { common } from '@/api/common';
    
    export default class Tree extends Vue {
        async getTreeData() {
            let[err, data] = await this.$to(common.tree({
                data: {}
            }));
            if (err) {
                return;
            }
        }
    }

    至此一个缓存api请求的装饰器工具已经封装好了,在第一次请求时会向后台发送请求,之后只要是页面不是手动刷新,在vue当中在缓存时间内就会一直存在,可以通过promise.then()的方式来调用。

     

     

     

  • 相关阅读:
    LIS(nlogn) POJ 3903 Stock Exchange
    LCS(滚动数组) POJ 1159 Palindrome
    LCS(打印全路径) POJ 2264 Advanced Fruits
    3-16 提取任务(第6章)
    3-15 《元编程》第6章 3-16 hook method
    3-13《元编程》第5章Class Definitions 3-14(5-4Singleton Classes,2小时)3-15(3小时✅)
    3-11 《Ruby元编程》第4章block块 3-12
    3-9《元编程》第3章Tuesday:methods
    3-8《Ruby元编程》第二章对象模型
    2-27 最短路径《啊哈算法》2-28完成四种算法
  • 原文地址:https://www.cnblogs.com/ziyoublog/p/14685887.html
Copyright © 2020-2023  润新知