<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          利用LRU策略實現(xiàn)Axios請求緩存

          共 7051字,需瀏覽 15分鐘

           ·

          2021-09-28 09:54

          點擊上方藍色字體,選擇“標星公眾號”

          優(yōu)質(zhì)文章,第一時間送達

          業(yè)務(wù)場景

          前一段時間剛做完一個項目,先說一下業(yè)務(wù)場景,有別于其他的前端項目,這次的項目是直接調(diào)用第三方服務(wù)的接口,而我們的服務(wù)端只做鑒權(quán)和透傳,第三方為了靈活,把接口拆的很零散,所以這個項目就像扔給你一堆樂高顆粒讓你組裝成一個機器人。所以可以大概分析一下這個項目在請求接口時的一些特點,然后針對性的做一些優(yōu)化:

          1. 請求接口多,可能你的一個n個條目的列表本來一個接口搞定現(xiàn)在需要n*10個接口才能拿到完整的數(shù)據(jù),有些功能模塊可能需要請求成千上萬次接口;

          2. 基本都是get請求,只讀不寫;

          3. 接口調(diào)用重復率高,因為接口很細碎,所以可能有些常用的接口需要重復調(diào)用;

          4. 接口返回的數(shù)據(jù)實時性要求不高,第三方的數(shù)據(jù)不是實時更新的,可能一天或者一周才更新一次,但是第三方要求不能以任何的方式落庫。

          所以綜上分析,前端緩存成了一個可行性較高的優(yōu)化方案。

          解決方案

          前端的HTTP請求使用的是Axios,因此可以利用Axios的攔截器進行緩存的管理。梳理一下邏輯:

          1. 創(chuàng)建緩存對象;

          2. 請求發(fā)起之前判斷該請求是否命中緩存:

            1. 是,直接返回緩存內(nèi)容;

            2. 否,發(fā)起請求,請求成功后將請求結(jié)果存入緩存中。

          如標題所說,這里的緩存策略我們用的是LRU(Least Recently Used)策略,因為緩存不能無限大,過大的緩存可能會導致瀏覽器頁面性能下降,甚至內(nèi)存泄漏。LRU會在緩存達到最大承載量后刪除最近最少使用的緩存內(nèi)容,因此不用擔心緩存無限增大。那么如何實現(xiàn)LRU緩存策略呢?Github上有現(xiàn)成的輪子,但是為了更深入的學習嘛,我們自己來手動實現(xiàn)一個。

          實現(xiàn)LRU

          LRU主要有兩個功能,存、取。梳理一下邏輯:

          1. 存入:

            1. 如果緩存已滿,刪除最近最少使用的緩存內(nèi)容,把當前的緩存存進去,放到最常用的位置;

            2. 否則直接將緩存存入最常用的位置。

          2. 讀取:

            1. 如果存在這個緩存,返回緩存內(nèi)容,同時把該緩存放到最常用的位置;

            2. 如果沒有,返回-1。

          這里我們可以看到,緩存是有優(yōu)先級的,我們用什么來標明優(yōu)先級呢?如果用數(shù)組存儲可以將不常用的放到數(shù)組的頭部,將常用的放到尾部。但是鑒于數(shù)據(jù)的插入效率不高,這里我們使用Map對象來作為容器存儲緩存。

          代碼如下:

          class LRUCache {
              constructor(capacity) {
                  if (typeof capacity !== 'number' || capacity < 0) {
                      throw new TypeError('capacity必須是一個非負數(shù)');
                  }
                  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);
              }
          }


          結(jié)合Axios實現(xiàn)請求緩存

          理一下大概的邏輯:每次請求根據(jù)請求的方法、url、參數(shù)生成一串hash,緩存內(nèi)容為hash->response,后續(xù)請求如果請求方法、url、參數(shù)一致,即認為命中緩存。

          代碼如下:

          import axios from 'axios';
          import md5 from 'md5';
          import LRUCache from './LRU.js';

          const cache = new LRUCache(100);

          const _axios = axios.create();

          // 將請求參數(shù)排序,防止相同參數(shù)生成的hash不同
          function sortObject(obj = {}) {
              let result = {};
              Object.keys(obj)
                  .sort()
                  .forEach((key) => {
                      result[key] = obj[key];
                  });
          }

          // 根據(jù)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) {
                  // 設(shè)置緩存
                  const hashKey = genHashByConfig(response.config);
                  cache.set(hashKey, response.data);
                  return response.data;
              },
              function(error) {
                  return Promise.reject(error);
              }
          );

          // 將axios請求封裝,如果命中緩存就不需要發(fā)起http請求,直接返回緩存內(nèi)容
          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,
              });
          }

          這里需要注意的一點是,我將請求方法,url,參數(shù)進行了hash操作,為了防止參數(shù)的順序改變而導致hash結(jié)果不一致,我在hash操作之前,給參數(shù)做了排序處理,實際開發(fā)中,參數(shù)的類型也不一定就是object,可以根據(jù)自己的需求進行改造。

          如上改造后,第一次請求后,相同的請求再次觸發(fā)就不會發(fā)送http請求了,而是直接從緩存中獲取,真是多快好省~



            作者 |  Rain Watcher

          來源 |  cnblogs.com/rain-watcher/p/15087066.html



          瀏覽 52
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩淫淫网 | 婷婷干| 爱啪啪导航| 免费的色情视频 | 大香蕉大香蕉大香蕉 |