zoukankan      html  css  js  c++  java
  • 利用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()的方式来调用。

     

     

     

  • 相关阅读:
    Flutter 布局类组件:简介
    Flutter 基础组件:进度指示器
    Flutter 基础组件:单选框和复选框
    Flutter 基础组件:图片和Icon
    Flutter 基础组件:按钮
    Flutter 基础组件:文本、字体样式
    Flutter 基础组件:状态管理
    Flutter 基础组件:Widget简介
    网络编程之TCP
    入门多线程
  • 原文地址:https://www.cnblogs.com/ziyoublog/p/14685887.html
Copyright © 2011-2022 走看看