Axios 如何缓存请求数据?

大家好,我是若川。欢迎加我微信 ruochuan12,长期交流学习。今天推荐这篇Axios缓存请求数据的文章,相信是常见的业务场景,感兴趣的读者可以看看 umi-request,支持缓存功能。另外我之前也写过 axios源码文章,是转载次数(16次)最多的一篇文章,好久没写好文章了,惭愧惭愧。

点击下方卡片关注我,或者查看源码等系列文章。学习源码整体架构系列、年度总结、JS基础系列

在 Axios 如何取消重复请求? 这篇文章中,阿宝哥介绍了在 Axios 中如何取消重复请求及 CancelToken 的工作原理。本文将介绍在 Axios 中如何通过增强默认适配器来缓存请求数据。那么为什么要缓存请求数据呢?这是因为在缓存未失效时,我们可以直接使用已缓存的数据,而不需发起请求从服务端获取数据,这样不仅可以减少 HTTP 请求而且还能减少等待时间从而提高用户体验。

因为本文将使用 Axios 提供的默认适配器来实现缓存请求数据的功能,所以如果你对 Axios 适配器还不熟悉的话,建议先阅读 77.9K 的 Axios 项目有哪些值得借鉴的地方 这篇文章。为了让大家能够更好地理解后续的内容,我们先来看一下整体的流程图:

上图中蓝色部分的工作流程,就是本文的重点。接下来,阿宝哥将从如何设计缓存开始,带大家一起来开发缓存请求数据的功能。

一、如何设计缓存

在计算中,缓存是一个高速数据存储层,其中存储了数据子集,且通常是 短暂性 存储,这样日后再次请求该数据时,速度要比访问数据的主存储位置快。通过缓存,你可以高效地重用之前检索或计算的数据。了解完缓存的作用之后,我们来设计缓存的 API:

  • get(key):从缓存中获取指定 key 对应的值;

  • delete(key):从缓存中删除指定 key 对应的值;

  • clear():清空已缓存的数据;

  • set(key, value, maxAge):保存键值对,同时支持设置缓存的最大时间,即 maxAge 单位为毫秒。

基于上述的缓存 API,我们可以实现一个简单的缓存功能,具体代码如下所示:

const MemoryCache = {data: {},set(key, value, maxAge) { // 保存数据this.data[key] = {maxAge: maxAge || 0,value,now: Date.now(),};},get(key) { // 从缓存中获取指定 key 对应的值。const cachedItem = this.data[key];if (!cachedItem) return null;const isExpired = Date.now() - cachedItem.now > cachedItem.maxAge;isExpired && this.delete(key);return isExpired ? null : cachedItem.value;},delete(key) { // 从缓存中删除指定 key 对应的值。return delete this.data[key];},clear() { // 清空已缓存的数据。this.data = {};},
};

其实除了自定义缓存对象之外,你也可以使用成熟的第三方库,比如 lru-cache。

LRU 缓存淘汰算法就是一种常用策略。LRU 的全称是 Least Recently Used,也就是说我们认为最近使用过的数据应该是是「有用的」,很久都没用过的数据应该是无用的,内存满了就优先删那些很久没用过的数据。

二、如何增强默认适配器

Axios 引入了适配器,使得它可以同时支持浏览器和 Node.js 环境。对于浏览器环境来说,它通过封装 XMLHttpRequest API 来发送 HTTP 请求,而对于 Node.js 环境来说,它通过封装 Node.js 内置的 http 和 https 模块来发送 HTTP 请求。

在介绍如何增强默认适配器之前,我们先来回顾一下 Axios 完整请求的流程:

了解完 Axios 完整请求的流程之后,我们再来看一下 Axios 内置的 xhrAdapter 适配器,它被定义在 lib/adapters/xhr.js 文件中:

// lib/adapters/xhr.js
module.exports = function xhrAdapter(config) {return new Promise(function dispatchXhrRequest(resolve, reject) {var requestData = config.data;var requestHeaders = config.headers;var request = new XMLHttpRequest();// 省略大部分代码var fullPath = buildFullPath(config.baseURL, config.url);request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);// Set the request timeout in MSrequest.timeout = config.timeout;// Listen for ready staterequest.onreadystatechange = function handleLoad() { ... }// Send the requestrequest.send(requestData);});
};

很明显 xhrAdapter 适配器是一个函数对象,它接收一个 config 参数并返回一个 Promise 对象。而在 xhrAdapter 适配器内部,最终会使用 XMLHttpRequest API 来发送 HTTP 请求。为了实现缓存请求数据的功能,我们就可以考虑通过高阶函数来增强 xhrAdapter 适配器的功能。

2.1 定义辅助函数

2.1.1 定义 generateReqKey 函数

在增强 xhrAdapter 适配器之前,我们先来定义一个 generateReqKey 函数,该函数用于根据当前请求的信息,生成请求 Key;

function generateReqKey(config) {const { method, url, params, data } = config;return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
}

通过 generateReqKey 函数生成的请求 key,将作为缓存项的 key,而对应的 value 就是默认 xhrAdapter 适配器返回的 Promise 对象。

2.1.2 定义 isCacheLike 函数

isCacheLike 函数用于判断传入的 cache 参数是否实现了前面定义的 Cache API,利用该函数,我们允许用户为每个请求自定义 Cache 对象。

function isCacheLike(cache) {return !!(cache.set && cache.get && cache.delete && cache.clear  && typeof cache.get === 'function' && typeof cache.set === 'function' && typeof cache.delete === 'function' && typeof cache.clear === 'function');
}

2.2 定义 cacheAdapterEnhancer 函数

为了让用户能够更灵活地控制数据缓存的功能,我们定义了一个 cacheAdapterEnhancer 函数,该函数支持两个参数:

  • adapter:预增强的 Axios 适配器对象;

  • options:缓存配置对象,该对象支持 4 个属性,分别用于配置不同的功能:

    • maxAge:全局设置缓存的最大时间;

    • enabledByDefault:是否启用缓存,默认为 true;

    • cacheFlag:缓存标志,用于配置请求 config 对象上的缓存属性;

    • defaultCache:用于设置使用的缓存对象。

了解完 cacheAdapterEnhancer 函数的参数之后,我们来看一下该函数的具体实现:

function cacheAdapterEnhancer(adapter, options) {const { maxAge, enabledByDefault = true,cacheFlag = "cache", defaultCache = MemoryCache,} = options;return (config) => {const { url, method, params, forceUpdate } = config;let useCache = config[cacheFlag] !== undefined && config[cacheFlag] !== null? config[cacheFlag]: enabledByDefault;if (method === "get" && useCache) {const cache = isCacheLike(useCache) ? useCache : defaultCache;let requestKey = generateReqKey(config);  // 生成请求Keylet responsePromise = cache.get(requestKey); // 从缓存中获取请求key对应的响应对象if (!responsePromise || forceUpdate) { // 缓存未命中/失效或强制更新时,则重新请求数据responsePromise = (async () => {try {return await adapter(config);  // 使用默认的xhrAdapter发送请求} catch (reason) {cache.delete(requestKey);throw reason;}})();cache.set(requestKey, responsePromise, maxAge);  // 保存请求返回的响应对象return responsePromise; // 返回已保存的响应对象}return responsePromise;}return adapter(config); // 使用默认的xhrAdapter发送请求};
}

以上的代码并不会复杂,核心的处理逻辑如下图所示:

2.3 使用 cacheAdapterEnhancer 函数

2.3.1 创建 Axios 对象并配置 adapter 选项
const http = axios.create({baseURL: "https://jsonplaceholder.typicode.com",adapter: cacheAdapterEnhancer(axios.defaults.adapter, {enabledByDefault: false, // 默认禁用缓存maxAge: 5000, // 缓存时间为5s}),
});
2.3.2 使用 http 对象发送请求
// 使用缓存
async function requestWithCache() {const response = await http.get("/todos/1", { cache: true });console.dir(response);
}// 不使用缓存
async function requestWithoutCache() {const response = await http.get("/todos/1", { cache: false });console.dir(response);
}

其实 cache 属性除了支持布尔值之外,我们可以配置实现 Cache API 的缓存对象,具体的使用示例如下所示:

const customCache = { get() {/*...*/}, set() {/*...*/}, delete() {/*...*/}, clear() {/*...*/}};async function requestForceUpdate() {const response = await http.get("/todos/1", {cache: customCache,forceUpdate: true,});console.dir(response);
}

好了,如何通过增强 xhrAdapter 适配器来实现 Axios 缓存请求数据的功能已经介绍完了。由于完整的示例代码内容比较多,阿宝哥就不放具体的代码了。感兴趣的小伙伴,可以访问以下地址浏览示例代码。

完整的示例代码:https://gist.github.com/semlinker/b8a7bd5a0a16c2d04011c2c4a8167fbd

三、总结

本文介绍了在 Axios 中如何缓存请求数据及如何设计缓存对象,基于文中定义的 cacheAdapterEnhancer 函数,你可以轻松地扩展缓存的功能。在后续的文章中,阿宝哥将会介绍在 Axios 中如何实现请求重试功能,感兴趣的小伙伴不要错过哟。另外,如果你对 Axios 如何取消重复请求感兴趣,可以阅读 Axios 如何取消重复请求? 这篇文章。

四、参考资源

  • 77.9K 的 Axios 项目有哪些值得借鉴的地方

  • Axios 如何取消重复请求?

  • Github - axios-extensions


最近组建了一个江西人的前端交流群,如果你也是江西人可以加我微信 ruochuan12 拉你进群。


················· 若川出品 ·················

今日话题

建的江西前端交流群,感觉氛围不错,想着要不要建其他省份的交流群,不过我是江西人,建其他省份的会不会有人进群是个问题~。欢迎分享、收藏、点赞、在看我的公众号文章~

一个愿景是帮助5年内前端人走向前列的公众号

可加我个人微信 ruochuan12,长期交流学习

推荐阅读

我在阿里招前端,我该怎么帮你?(现在还能加我进模拟面试群)

如何拿下阿里巴巴 P6 的前端 Offer

点击方卡片关注我,或者查看源码等系列文章。
学习源码整体架构系列、年度总结、JS基础系列

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/275992.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Reflection in .net

最近在看设计模式的一些东西,在讲到builder模式中使用到了c#的反射机制,从网上找了写资料,整理如下: 在C#中,我们要使用反射,首先要搞清楚以下命名空间中几个类的关系: System.Reflection命名空间(1) App…

山西万荣盛装迎新春 首届群众文化艺术节启幕

山西万荣盛装迎新春,首届群众文化艺术节启幕。 李继旺 摄 山西万荣盛装迎新春,首届群众文化艺术节启幕。 李继旺 摄 中新网太原1月29日电 (任丽娜)璀璨闪耀的宝鼎广场花灯展、激情活力的群众文体赛事、精彩纷呈的红火热闹、精品年货展销、李家大院游园会…

MySQL 隐式转换 字符串和整型说明

MySQL 隐式转换 字段类型定义 CREATE TABLE user (id int(10) NOT NULL AUTO_INCREMENT COMMENT 编号,/* ...... */name varchar(10) NOT NULL DEFAULT COMMENT 姓名,/* ...... */PRIMARY KEY (id),KEY idx_name (name,nickname),/* ...... */ ) ENGINEInnoDB DEFAULT CHARSE…

安装vs2017出现闪退现象_Adobe Reader 闪退

在我们的日常办公、学习中经常会涉及到 PDF 文件,免不了需要处理 PDF 文件如:PDF转Word、PDF合并、导出成图片、PDF解密、PDF转PPT、PDF加水印、PDF签名等等。PDF是与平台无关的文件格式,无论在哪种打印机上都可保证精确的颜色和准确的打印效…

从Vue.js源码中我学到的几个实用函数

大家好,我是若川。欢迎加我微信 ruochuan12,长期交流学习。今天推荐Vuejs源码中几个实用的方法。如果想看Vuejs源码,不知道如何下手,一般推荐配置Sourcemap,针对单个问题调试来看,如何调试Vuejs源码&#x…

real类型_如何使用REAL方法对您的Web内容进行现实检查

real类型Web内容审核模板 (Web content audit template) I recently completed a website audit project for a nonprofit organization through CatchAFire.org. As part of the website audit, I audited every key page’s content, looking for areas of opportunity.我最近…

青海行--(7月26日)翻越祁连山

嘉峪关是最值得一写的,虽然也是著名的旅游城嘉峪关景点是国家AAAAA级景区,但与敦煌完全不一样,没有人山人海的游客,门票也不贵,才10&a…

TikTok真题第1天 | 666.路径和IV、 207.课程表、210.课程表||

666.路径和IV 题目链接:666.路径和IV 解法: 参考这篇题解:【LeetCode - 666】路径和 IV_力扣666路径总和4-CSDN博客 关键点在于: (1)使用map来存node:key 为整数的前两位,value…

导出Excel

2019独角兽企业重金招聘Python工程师标准>>> 思路: 1, 引入Excel类库; 2, 创建一个模板; 3, 将数据填充进去; 4, 生成文件; 下面是一个简单的示例 $phpExcelObj new PHPExcel(); $titleMap self::TITLE_MAP; //设置表头 $i 0; foreach ($titleMap as $key > $…

CentOS系统更换yum源(repomd.xml not found解决方案)

CentOS系统更换yum源 问题 当初瞎鼓捣服务器,更换yum源为aliyun的,奈何阿里的源最近全部打不开,导致yum安装不了,一直报错: http://mirrors.aliyun.com/centos/6/os/x86_64/repodata/repomd.xml: [Errno 14] PYCURL E…

在类中用class时数据是共有还是私有_jvm学习笔记之class文件的加载、初始化

编写的java文件在要真正运行时,会首先被编译成 “.class"结尾的二进制文件,然后被虚拟机加载。那么在虚拟机中一个class文件要成为java实例,需要经历好几个步骤:1、装载:装载阶段由三个基本动作完成,要…

所有前端都要看的2D游戏化互动入门基础知识

背景现在越来越多的公司和APP开始使用游戏化的方式去做产品了,所谓游戏化,是指在非游戏环境中将游戏的思维和游戏的机制进行整合运用,以引导用户互动和使用的方法。支付宝里面的蚂蚁庄园、蚂蚁森林,通过游戏和公益的结合实现用户的…

江苏一动物园现“旋转活马” 园方:创意来自马术训练

中新网南通1月31日电 (记者唐娟)“旋转马设备采用同时容纳六匹马的遛马器组装而成,对马匹没有任何伤害,初衷是希望给小朋友一种全新体验,这才有了这个创意项目。”1月31日,针对活马版“旋转木马”引发的热议,江苏南通森…

Byte数组转换成string 的方法积累

.net的加密算法,返回的都是byte[] 类型,在存贮起来让人非常头疼,最简单的方法就是把byte[]转换成string来存贮,当然如果数据量大的话,另当别论。 所以我就把byte[]转换成string的方法做一个简单的积累与分析。目前有3种…

加快信息化建设对地方发展的_加快设计师职业发展的9种方法

加快信息化建设对地方发展的重点 (Top highlight)Over the past few months, I have had an increase in conversations with design students from various institutions, as well as early, to senior-level designers, researchers, & product managers from various co…

Docker:Nginx-Redis-Mysql-PHP 部署

Docker:Nginx-Redis-Mysql-PHP 部署 网络桥接 Docker容器之间默认网络隔离,需要使用桥接网络进行互通 创建网络 docker network create net-local docker network ls NETWORK ID NAME DRIVER SCOPE da9c8fc3dc80 bridge bridge local 78641…

epoll监听文件_介绍一下 Android Handler 中的 epoll 机制?

介绍一下 Android Handler 中的 epoll 机制?目录:IO 多路复用select、poll、epoll 对比epoll APIepoll 使用示例Handler 中的 epoll 源码分析IO 多路复用IO 多路复用是一种同步 IO 模型,实现一个线程可以监视多个文件句柄。一旦某个文件句柄就…

前端工程师的一大神器——puppeteer

大家好,我是若川。欢迎加我微信 ruochuan12,长期交流学习。今天推荐神器puppeteer,我猜有挺多人不知道。文章不长,看完有空也可以试玩。我18年也写过一篇puppeteer爬取生成pdf的文章,时间真快。前端使用puppeteer 爬虫…

selenium界面元素定位

一、 Selenium界面元素定位 本文元素定位以das2为例 #导入包 from selenium import webdriver #打开火狐驱动 driverwebdriver.Firefox() #访问网址 driver.get("http://192.168.3.217:8080/das/seatlogin.jsp ") 进行web页面自动化测试,对页面上…

vue.js ui_UI / UX开发:考虑Vue.js

vue.js uiBecause sometimes we have to add logic to our concepts, and Vue makes it a whole lot easier.因为有时我们必须在概念中添加逻辑,而Vue使其变得更加容易。 FULL DISCLOSURE: THIS IS NOT A COMPLETE JAVASCRIPT OR VUE COURSE. There’s no way I co…