<acronym id="indot"><dfn id="indot"></dfn></acronym>
<span id="indot"></span>

<bdo id="indot"><meter id="indot"></meter></bdo>
<label id="indot"><samp id="indot"></samp></label>
<label id="indot"><xmp id="indot">
  • <span id="indot"><table id="indot"></table></span>
    <center id="indot"><optgroup id="indot"></optgroup></center>
  • <bdo id="indot"><meter id="indot"></meter></bdo>
      當(dāng)前位置:首頁 > javascript > vue > 正文內(nèi)容

      axios性能優(yōu)化:防止重復(fù)請(qǐng)求

      hxing6412年前 (2024-01-02)vue3804

      網(wǎng)站性能優(yōu)化是一系列技術(shù)和策略的應(yīng)用,提高網(wǎng)站的加載速度、響應(yīng)時(shí)間和整體性能,以提供更好的用戶體驗(yàn)和增強(qiáng)網(wǎng)站的競爭力,比較直接的方式就是減少http請(qǐng)求數(shù)量,過濾掉無效請(qǐng)求。

      也是本篇文章的主題。


      開始

      本篇將基于axios開源庫,對(duì)http請(qǐng)求進(jìn)行封裝,包含請(qǐng)求的緩存、重復(fù)請(qǐng)求的過濾兩個(gè)小優(yōu)化。


      第一步


      首先建立一個(gè) http-helper.js 文件,里面將基于axios進(jìn)行上述相關(guān)功能的封裝


      首先里面的內(nèi)容大概是這樣的:


      import axios from 'axios';
      const http = axios.create();
      export default http


      上述就是簡單地導(dǎo)出了一個(gè)axios實(shí)例,供項(xiàng)目使用


      增加請(qǐng)求緩存功能


      那么有了緩存功能,就要對(duì)緩存命中進(jìn)行定義,我定義的緩存命中是指:http請(qǐng)求的url相同、請(qǐng)求參數(shù)相同、請(qǐng)求類型相同,以上三者都相同的情況下,就視為緩存允許命中,最后根據(jù)緩存過期時(shí)間,判斷是否獲取最新數(shù)據(jù),還是從緩存中取。

      下面理一下流程:


      1. 發(fā)起請(qǐng)求,設(shè)置請(qǐng)求是否緩存,緩存多長時(shí)間

      2. axios請(qǐng)求攔截,判斷該請(qǐng)求是否設(shè)置緩存,是?則判斷是否緩存命中、是否過期,否?則繼續(xù)發(fā)起請(qǐng)求

      3. axios響應(yīng)攔截,判斷該請(qǐng)求結(jié)果是否緩存,是?則緩存數(shù)據(jù),并設(shè)置key值、過期時(shí)間


      針對(duì)上面的流程,需要有幾點(diǎn)確認(rèn)一下:


      當(dāng)緩存命中時(shí),如何終止請(qǐng)求


      1. axios中,可以為每一個(gè)請(qǐng)求設(shè)置一個(gè)cancleToken,當(dāng)調(diào)用請(qǐng)求取消方法的時(shí)候,則請(qǐng)求終止,并將終止的消息通過reject回傳給請(qǐng)求方法。

      2. 當(dāng)緩存命中時(shí),并將緩存的數(shù)據(jù)通過 resolve() 返回給請(qǐng)求方法,而不是在 reject 中獲取緩存數(shù)據(jù)


      那么具體的代碼可以是這樣的:


      // http-helper.js
      import axios from 'axios';
      const http = axios.create();
      http.interceptors.request.use((config) => {
          /**
           * 為每一次請(qǐng)求生成一個(gè)cancleToken
           */
          const source = axios.CancelToken.source();
          config.cancelToken = source.token;
          /**
           * 嘗試獲取緩存數(shù)據(jù)
           */
          const data = storage.get(cryptoHelper.encrypt(
              config.url + JSON.stringify(config.data) + (config.method || ''),
          ));
          /** 
          * 判斷緩存是否命中,是否未過期
          */
          if (data && (Date.now() <= data.exppries)) {
              console.log(`接口:${config.url} 緩存命中 -- ${Date.now()} -- ${data.exppries}`);
              /**
              * 將緩存數(shù)據(jù)通過cancle方法回傳給請(qǐng)求方法
              */
              source.cancel(JSON.stringify({
                  type: CANCELTTYPE.CACHE,
                  data: data.data,
              }));
          }
          return config;
      });
      http.interceptors.response.use((res) => {
          if (res.data && res.data.type === 0) {
              if (res.config.data) {
                  /**
                  * 獲取請(qǐng)求體參數(shù)
                  */
                  const dataParse = JSON.parse(res.config.data);
                  if (dataParse.cache) {
                      if (!dataParse.cacheTime) {
                          dataParse.cacheTime = 1000 * 60 * 3;
                      }
                      /**
                      * 加密
                      * 緩存
                      */
                      storage.set(cryptoHelper.encrypt(res.config.url + res.config.data + (res.config.method || '')), {
                          data: res.data.data, // 響應(yīng)體數(shù)據(jù)
                          exppries: Date.now() + dataParse.cacheTime, // 設(shè)置過期時(shí)間
                      });
                      console.log(`接口:${res.config.url} 設(shè)置緩存,緩存時(shí)間: ${dataParse.cacheTime}`);
                  }
              }
              return res.data.data;
          } else {
              return Promise.reject('接口報(bào)錯(cuò)了!');
          }
      });
      /**
       * 封裝 get、post 請(qǐng)求
       * 集成接口緩存過期機(jī)制
       * 緩存過期將重新請(qǐng)求獲取最新數(shù)據(jù),并更新緩存
       * 數(shù)據(jù)存儲(chǔ)在localstorage
       * {
       *      cache: true
       *      cacheTime: 1000 * 60 * 3  -- 默認(rèn)緩存3分鐘
       * }
       */
      const httpHelper = {
          get(url, params) {
              return new Promise((resolve, reject) => {
                  http.get(url, params).then(async (res) => {
                      resolve(res);
                  }).catch((error) => {
                      if (axios.isCancel(error)) {
                          const cancle = JSON.parse(error.message);
                          if (cancle.type === CANCELTTYPE.REPEAT) {
                              return resolve([]);
                          } else {
                              return resolve(cancle.data);
                          }
                      } else {
                          return reject(error);
                      }
                  });
              });
          },
          post(url: string, params: any) {
              return new Promise((resolve, reject) => {
                  http.post(url, params).then(async (res) => {
                      resolve(res);
                  }).catch((error: AxiosError) => {
                      if (axios.isCancel(error)) {
                          const cancle = JSON.parse(error.message);
                          if (cancle.type === CANCELTTYPE.REPEAT) {
                              return resolve(null);
                          } else {
                              return resolve(cancle.data);
                          }
                      } else {
                          return reject(error);
                      }
                  });
              });
          },
      };
      export default httpHelper


      上面代碼中,有些東西沒有解釋到:

      1. 其中storage是自己封裝的緩存數(shù)據(jù)類,可以有.get、.set等方法,cryptoHelper是封裝的MD5加密庫,主要是通過MD5加密請(qǐng)求url、請(qǐng)求數(shù)據(jù)、請(qǐng)求類型等拼接的字符串,通過加密后的key來獲取緩存中的數(shù)據(jù)(因?yàn)槠唇雍蟮淖址L,通過MD5加密一下,會(huì)短很多)


      2. 為什么要單獨(dú)封裝一個(gè) httpHelper,因?yàn)閍xios.CancelToken.source().cancle(***)中的信息,只能在reject中取到,為了緩存命中時(shí),仍然能在then中獲取到正確的數(shù)據(jù),則需要單獨(dú)處理一下這個(gè)情況。


      增加重復(fù)請(qǐng)求過濾功能

      規(guī)則: 以最新的請(qǐng)求為主,即最新的重復(fù)請(qǐng)求,會(huì)將之前的重復(fù)請(qǐng)求中斷掉

      大概流程如下:

      1. 發(fā)起請(qǐng)求

      2. axios請(qǐng)求攔截,判斷請(qǐng)求列表數(shù)組中,是否存在相同的請(qǐng)求,是?終止之前所有重復(fù)請(qǐng)求,否?將當(dāng)次請(qǐng)求添加進(jìn)請(qǐng)求數(shù)組中,最終都繼續(xù)會(huì)請(qǐng)求

      3. axios響應(yīng)攔截器,將當(dāng)次請(qǐng)求從請(qǐng)求數(shù)組中刪除


      具體代碼如下:


      // http-helper.js
      import axios from 'axios';
      const http = axios.create();
      const pendingRequests = [];
      http.interceptors.request.use((config) => {
          /**
           * 為每一次請(qǐng)求生成一個(gè)cancleToken
           */
          const source = axios.CancelToken.source();
          config.cancelToken = source.token;
          // .... 省略部分代碼
          /**
           * 重復(fù)請(qǐng)求判斷
           * 同url,同請(qǐng)求類型判定為重復(fù)請(qǐng)求
           * 以最新的請(qǐng)求為準(zhǔn)
           */
          const md5Key = cryptoHelper.encrypt(config.url + (config.method || ''));
          /**
           * 將之前的重復(fù)且未完成的請(qǐng)求全部取消
           */
          const hits = pendingRequests.filter((item) => item.md5Key === md5Key);
          if (hits.length > 0) {
              hits.forEach((item) => item.source.cancel(JSON.stringify({
                  type: CANCELTTYPE.REPEAT,
                  data: '重復(fù)請(qǐng)求,以取消',
              })));
          }
          /**
           * 將當(dāng)前請(qǐng)求添加進(jìn)請(qǐng)求對(duì)列中
           */
          pendingRequests.push({
              md5Key,
              source,
          });
          return config;
      });
      http.interceptors.response.use((res) => {
          /**
           * 不論請(qǐng)求是否成功,
           * 將本次完成的請(qǐng)求從請(qǐng)求隊(duì)列中移除
           */
          // 以同樣的加密方式(MD5)獲取加密字符串
          const md5Key = cryptoHelper.encrypt(res.config.url + (res.config.method || ''));
          const index = pendingRequests.findIndex((item) => item.md5Key === md5Key);
          if (index > -1) {
              pendingRequests.splice(index, 1);
          }
          // .... 省略部分代碼
      });
      // .... 省略部分代碼


      其實(shí)邏輯很簡單,通過一個(gè)數(shù)組去維護(hù)請(qǐng)求列表即可


      最終成果物

      是用ts寫的,需要使用可以改成 js

      由于緩存和終止重復(fù)請(qǐng)求,都需要用到source.cancle,因此需要一個(gè)type值,區(qū)分是緩存命中終止,還是重復(fù)請(qǐng)求終止,代碼中是CANCELTTYPE常量。


      **http-helper.ts **
      import axios, {CancelTokenSource, AxiosResponse, AxiosRequestConfig, AxiosError} from 'axios';
      import Storage from './storage-helper';
      import CryptoHelper from './cryptoJs-helper';
      const CANCELTTYPE = {
          CACHE: 1,
          REPEAT: 2,
      };
      interface ICancel {
          data: any;
          type: number;
      }
      interface Request {
          md5Key: string;
          source: CancelTokenSource;
      }
      const pendingRequests: Request[] = [];
      const http = axios.create();
      const storage = new Storage();
      const cryptoHelper = new CryptoHelper('cacheKey');
      http.interceptors.request.use((config: AxiosRequestConfig) => {
          /**
           * 為每一次請(qǐng)求生成一個(gè)cancleToken
           */
          const source = axios.CancelToken.source();
          config.cancelToken = source.token;
          /**
           * 緩存命中判斷
           * 成功則取消當(dāng)次請(qǐng)求
           */
          const data = storage.get(cryptoHelper.encrypt(
              config.url + JSON.stringify(config.data) + (config.method || ''),
          ));
          if (data && (Date.now() <= data.exppries)) {
              console.log(`接口:${config.url} 緩存命中 -- ${Date.now()} -- ${data.exppries}`);
              source.cancel(JSON.stringify({
                  type: CANCELTTYPE.CACHE,
                  data: data.data,
              }));
          }
          /**
           * 重復(fù)請(qǐng)求判斷
           * 同url,同請(qǐng)求類型判定為重復(fù)請(qǐng)求
           * 以最新的請(qǐng)求為準(zhǔn)
           */
          const md5Key = cryptoHelper.encrypt(config.url + (config.method || ''));
          /**
           * 將之前的重復(fù)且未完成的請(qǐng)求全部取消
           */
          const hits = pendingRequests.filter((item) => item.md5Key === md5Key);
          if (hits.length > 0) {
              hits.forEach((item) => item.source.cancel(JSON.stringify({
                  type: CANCELTTYPE.REPEAT,
                  data: '重復(fù)請(qǐng)求,以取消',
              })));
          }
          /**
           * 將當(dāng)前請(qǐng)求添加進(jìn)請(qǐng)求對(duì)列中
           */
          pendingRequests.push({
              md5Key,
              source,
          });
          return config;
      });
      http.interceptors.response.use((res: AxiosResponse) => {
          /**
           * 不論請(qǐng)求是否成功,
           * 將本次完成的請(qǐng)求從請(qǐng)求隊(duì)列中移除
           */
          // 以同樣的加密方式(MD5)獲取加密字符串
          const md5Key = cryptoHelper.encrypt(res.config.url + (res.config.method || ''));
          const index = pendingRequests.findIndex((item) => item.md5Key === md5Key);
          if (index > -1) {
              pendingRequests.splice(index, 1);
          }
          if (res.data && res.data.type === 0) {
              if (res.config.data) {
                  const dataParse = JSON.parse(res.config.data);
                  if (dataParse.cache) {
                      if (!dataParse.cacheTime) {
                          dataParse.cacheTime = 1000 * 60 * 3;
                      }
                      storage.set(cryptoHelper.encrypt(res.config.url + res.config.data + (res.config.method || '')), {
                          data: res.data.data,
                          exppries: Date.now() + dataParse.cacheTime,
                      });
                      console.log(`接口:${res.config.url} 設(shè)置緩存,緩存時(shí)間: ${dataParse.cacheTime}`);
                  }
              }
              return res.data.data;
          } else {
              return Promise.reject('接口報(bào)錯(cuò)了!');
          }
      });
      /**
       * 封裝 get、post 請(qǐng)求
       * 集成接口緩存過期機(jī)制
       * 緩存過期將重新請(qǐng)求獲取最新數(shù)據(jù),并更新緩存
       * 數(shù)據(jù)存儲(chǔ)在localstorage
       * {
       *      cache: true
       *      cacheTime: 1000 * 60 * 3  -- 默認(rèn)緩存3分鐘
       * }
       */
      const httpHelper = {
          get(url: string, params: any) {
              return new Promise((resolve, reject) => {
                  http.get(url, params).then(async (res: AxiosResponse) => {
                      resolve(res);
                  }).catch((error: AxiosError) => {
                      if (axios.isCancel(error)) {
                          const cancle: ICancel = JSON.parse(error.message);
                          if (cancle.type === CANCELTTYPE.REPEAT) {
                              return resolve([]);
                          } else {
                              return resolve(cancle.data);
                          }
                      } else {
                          return reject(error);
                      }
                  });
              });
          },
          post(url: string, params: any) {
              return new Promise((resolve, reject) => {
                  http.post(url, params).then(async (res: AxiosResponse) => {
                      resolve(res);
                  }).catch((error: AxiosError) => {
                      if (axios.isCancel(error)) {
                          const cancle: ICancel = JSON.parse(error.message);
                          if (cancle.type === CANCELTTYPE.REPEAT) {
                              return resolve(null);
                          } else {
                              return resolve(cancle.data);
                          }
                      } else {
                          return reject(error);
                      }
                  });
              });
          },
      };
      export default httpHelper;
      cryptoJs-helper.ts
      import cryptoJs from 'crypto-js';
      class CryptoHelper {
          public key: string;
          constructor(key: string) {
              /**
              * 如需秘鑰,可以在實(shí)例化時(shí)傳入
              */
              this.key = key;
          }
          /**
           * 加密
           * @param word
           */
          public encrypt(word: string | undefined): string {
              if (!word) {
                  return '';
              }
              const encrypted = cryptoJs.MD5(word);
              return encrypted.toString();
          }
      }
      export default CryptoHelper;
      storage-helper.ts
      class Storage {
          public get(key: string | undefined) {
              if (!key) { return; }
              const text = localStorage.getItem(key);
              try {
                  if (text) {
                      return JSON.parse(text);
                  } else {
                      localStorage.removeItem(key);
                      return null;
                  }
              } catch {
                  localStorage.removeItem(key);
                  return null;
              }
          }
          public set(key: string | undefined, data: any) {
              if (!key) {
                  return;
              }
              localStorage.setItem(key, JSON.stringify(data));
          }
          public remove(key: string | undefined) {
              if (!key) {
                  return;
              }
              localStorage.removeItem(key);
          }
      }
      export default Storage;


      掃描二維碼推送至手機(jī)訪問。

      版權(quán)聲明:本文由星星博客發(fā)布,如需轉(zhuǎn)載請(qǐng)注明出處。

      本文鏈接:http://www.7811333.com/?id=497

      “axios性能優(yōu)化:防止重復(fù)請(qǐng)求” 的相關(guān)文章

      vue3 插件開發(fā)

      vue3 插件開發(fā)

      插件是向vue添加全局功能。你如果是一個(gè)對(duì)象需要提供install方法,你如果是function就直接當(dāng)install 方法去使用。寫一個(gè)loading組件供全局使用:1)目錄結(jié)構(gòu)index.ts文件內(nèi)容:import { createVNode, render,&nb...

      uniapp 中 ScrollView 組件上拉分頁怎么不滾動(dòng)到最頂部

      實(shí)現(xiàn)類似微信聊天頁面,上拉加載更多歷史聊天記錄,每次上拉到頂部,界面自動(dòng)會(huì)滾動(dòng)到最頂部,我希望ScrollView不要滾動(dòng)到最頂部,每次就停留在當(dāng)前位置1,綁定scroll-view中scroll-into-view屬性<scroll-view class="scroll-...

      Vue.js雙向綁定的實(shí)現(xiàn)原理解析與實(shí)例演示

      1. 簡介 Vue.js作為一款流行的前端框架,其雙向綁定機(jī)制是其核心特性之一。通過雙向綁定,Vue.js實(shí)現(xiàn)了數(shù)據(jù)模型和視圖之間的即時(shí)同步,為開發(fā)者提供了極大的便利。在本文中,我們將深入探討Vue.js雙向綁定的實(shí)現(xiàn)原理,并結(jié)合代碼示例進(jìn)行詳細(xì)解釋。...

      Vue中的防抖節(jié)流技術(shù),提升用戶交互體驗(yàn)

      在Vue開發(fā)中,我們經(jīng)常會(huì)遇到需要處理用戶頻繁操作的場景,例如搜索框輸入聯(lián)想、窗口大小改變等。這時(shí),使用防抖節(jié)流技術(shù)能夠有效地控制事件觸發(fā)頻率,提升用戶體驗(yàn)和頁面性能。 1. 防抖技術(shù) 防抖(Debounce)是指在事件被觸發(fā)后,等...

      uniapp運(yùn)行APP報(bào)錯(cuò)reportJSException >>>> exception function:createInstanceContext, exception:white screen

      今天分享個(gè)自己在工作中遇到的頭疼問題,記錄下,在使用pinia時(shí),引入use模塊,h5端沒問題,我使用的是vue3,運(yùn)行在APP端一直報(bào)錯(cuò):reportJSException >>>> exception function:createInstanceContext, exc...

      vue 前端自動(dòng)打開文件地址進(jìn)行下載

      ue 前端自動(dòng)打開文件地址進(jìn)行下載   最近在做異步導(dǎo)出的功能,導(dǎo)出的過程中前端另外啟動(dòng)一個(gè)查詢導(dǎo)出進(jìn)度的線程接口。如果導(dǎo)出完成后,把生成的文件上傳到服務(wù)器,返回給前端一個(gè)文件的下載地址;前端自動(dòng)打開這個(gè)地址進(jìn)行跳轉(zhuǎn)下載。 有兩種方...

      發(fā)表評(píng)論

      訪客

      ◎歡迎參與討論,請(qǐng)?jiān)谶@里發(fā)表您的看法和觀點(diǎn)。
      主站蜘蛛池模板: 色天天天综合色天天碰| 91亚洲精品第一综合不卡播放| 久久综合狠狠色综合伊人| 在线亚洲97se亚洲综合在线| 精品综合久久久久久88小说| 久久综合精品视频| 六月婷婷缴清综合在线| 亚洲熟女乱综合一区二区| 亚洲综合一区二区| 中文字幕国产综合| 亚洲综合色丁香婷婷六月图片| 亚洲国产成人久久综合野外| 国产成人综合久久综合| 自拍三级综合影视| 狠狠综合久久av一区二区| 久久久综合亚洲色一区二区三区 | 亚洲精品天天影视综合网| 亚洲妓女综合网99| 丁香婷婷色五月激情综合深爱| 国产精品综合AV一区二区国产馆| 中文字幕久久综合| 久久综合亚洲色HEZYO国产| 婷婷综合久久中文字幕蜜桃三| 97色伦图片97综合影院| 亚洲狠狠综合久久| 亚洲色图综合网站| 色综合网站国产麻豆| 亚洲综合久久一本伊伊区| 色综合色综合色综合色欲| 亚洲国产成人久久综合一| 久久综合九色综合97免费下载| 狠狠色狠狠色综合网| 亚洲狠狠婷婷综合久久久久 | 在线亚洲97se亚洲综合在线| 国产综合精品一区二区三区| 99热婷婷国产精品综合| 亚洲精品综合一二三区在线 | 亚洲av日韩综合一区在线观看| 91精品国产综合久久久久久| 国产激情综合在线观看| 亚洲av综合avav中文|