本文是 Axios 四部曲的最后一篇文章,这篇文章将介绍在 Axios 中如何经过 加强默认适配器 来缓存请求数据。那么为何要缓存请求数据呢?这是由于在缓存未失效时,咱们能够直接使用已缓存的数据,而不需发起请求从服务端获取数据,这样不只能够减小 HTTP 请求并且还能减小等待时间从而提升用户体验。ios
这篇文章。为了让你们可以更好地理解后续的内容,咱们先来看一下总体的流程图:git
上图中蓝色部分的工做流程,就是本文的重点。接下来,阿宝哥将从如何设计缓存开始,带你们一块儿来开发缓存请求数据的功能。github
在计算中,缓存是一个高速数据存储层,其中存储了数据子集,且一般是 短暂性 存储,这样往后再次请求该数据时,速度要比访问数据的主存储位置快。经过缓存,你能够高效地重用以前检索或计算的数据。了解完缓存的做用以后,咱们来设计缓存的 API:算法
基于上述的缓存 API,咱们能够实现一个简单的缓存功能,具体代码以下所示:npm
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。json
LRU 缓存淘汰算法就是一种经常使用策略。LRU 的全称是 Least Recently Used,也就是说咱们认为最近使用过的数据应该是是「有用的」,好久都没用过的数据应该是无用的,内存满了就优先删那些好久没用过的数据。axios
Axios 引入了适配器,使得它能够同时支持浏览器和 Node.js 环境。对于浏览器环境来讲,它经过封装 XMLHttpRequest API 来发送 HTTP 请求,而对于 Node.js 环境来讲,它经过封装 Node.js 内置的 http 和 https 模块来发送 HTTP 请求。浏览器
在介绍如何加强默认适配器以前,咱们先来回顾一下 Axios 完整请求的流程:缓存
了解完 Axios 完整请求的流程以后,咱们再来看一下 Axios 内置的 xhrAdapter 适配器,它被定义在 lib/adapters/xhr.js 文件中:async
// 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 MS request.timeout = config.timeout; // Listen for ready state request.onreadystatechange = function handleLoad() { ... } // Send the request request.send(requestData); }); }; 复制代码
很明显 xhrAdapter 适配器是一个函数对象,它接收一个 config 参数并返回一个 Promise 对象。而在 xhrAdapter 适配器内部,最终会使用 XMLHttpRequest API 来发送 HTTP 请求。为了实现缓存请求数据的功能,咱们就能够考虑经过高阶函数来加强 xhrAdapter 适配器的功能。
关注「全栈修仙之路」阅读阿宝哥原创的 4 本免费电子书(累计下载 3万+)及 50 几篇 TS 系列教程。
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' ); } 复制代码
为了让用户可以更灵活地控制数据缓存的功能,咱们定义了一个 cacheAdapterEnhancer 函数,该函数支持两个参数:
了解完 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); // 生成请求Key let 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.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 缓存请求数据的功能已经介绍完了。因为完整的示例代码内容比较多,阿宝哥就不放具体的代码了。感兴趣的小伙伴,能够访问如下地址浏览示例代码。
完整的示例代码: http://github.crmeb.net/u/defu
本文介绍了在 Axios 中如何缓存请求数据及如何设计缓存对象,基于文中定义的 cacheAdapterEnhancer 函数,你能够轻松地扩展缓存的功能。至此 Axios 四部曲已经所有更新完成了,如下是其余文章的连接,感兴趣的小伙伴能够了解一下。写得很差的地方,请多多包涵。