本文由特邀作者Peter Bengtsson撰写。SitePoint特邀文章旨在为您带来来自JavaScript社区知名作家和演讲者的精彩内容
本文演示了如何实现已提取请求的本地缓存,以便如果重复执行,则从会话存储中读取。这样做的好处是,您无需为要缓存的每个资源编写自定义代码。
如果您想在下次JavaScript聚会上炫耀一番,展示您在处理Promise、最先进的API和本地存储方面的各种技能,请继续阅读。
主要收获
- 利用Fetch API,开发人员可以创建AJAX请求的本地缓存,通过减少冗余的网络调用和加快数据检索来提高效率。
- 使用全局变量进行缓存的简单方法受会话持久性的限制;切换到会话存储允许数据在同一会话中跨页面重新加载持久存在。
- 实现
cachedFetch
封装了标准的fetch
调用,可以根据内容类型和URL自动缓存响应,从而使缓存机制通用化。 -
cachedFetch
的增强功能包括在进行网络请求之前处理来自会话存储的缓存命中,以及管理内容过期以避免使用过时数据。 - 未来的改进可能包括处理二进制数据和使用哈希URL作为缓存键,以优化Web应用程序中的存储和检索过程。
Fetch API
此时,您应该熟悉fetch。它是浏览器中一个新的原生API,用于替换旧的XMLHttpRequest API。
Can I Use fetch? https://www.php.cn/link/b751ea087892ebeca363034301f45c69网站上关于主要浏览器对fetch功能支持的数据。
在并非所有浏览器都完美实现的地方,您可以使用GitHub的fetch polyfill(如果您整天无所事事,这里有Fetch标准规范)。
简单的替代方案
假设您确切知道需要下载哪个资源,并且只想下载一次。您可以使用全局变量作为缓存,如下所示:
let origin = null; fetch('https://httpbin.org/get') .then(r => r.json()) .then(information => { origin = information.origin; // 您的客户端IP }); // 需要延迟以确保fetch已完成 setTimeout(() => { console.log('您的来源是 ' + origin); }, 3000);
这仅仅依赖于全局变量来保存缓存的数据。直接的问题是,如果您重新加载页面或导航到新页面,缓存的数据就会消失。
在我们剖析其缺点之前,让我们升级一下第一个简单的解决方案。
fetch('https://httpbin.org/get') .then(r => r.json()) .then(info => { sessionStorage.setItem('information', JSON.stringify(info)); }); // 需要延迟以确保fetch已完成 setTimeout(() => { let info = JSON.parse(sessionStorage.getItem('information')); console.log('您的来源是 ' + info.origin); }, 3000);
第一个直接的问题是fetch是基于Promise的,这意味着我们无法确定它何时完成,因此为了确定起见,我们不应依赖于它的执行,直到它的Promise解析。
第二个问题是此解决方案非常特定于特定的URL和特定的缓存数据片段(在此示例中为关键信息)。我们想要的是一个基于URL的通用解决方案。
第一次实现 – 保持简单
让我们围绕fetch创建一个包装器,它也返回一个Promise。调用它的代码可能并不关心结果是来自网络还是来自本地缓存。
所以想象一下您曾经这样做:
let origin = null; fetch('https://httpbin.org/get') .then(r => r.json()) .then(information => { origin = information.origin; // 您的客户端IP }); // 需要延迟以确保fetch已完成 setTimeout(() => { console.log('您的来源是 ' + origin); }, 3000);
现在您想对其进行包装,以便重复的网络调用可以从本地缓存中获益。让我们简单地将其称为cachedFetch,因此代码如下所示:
fetch('https://httpbin.org/get') .then(r => r.json()) .then(info => { sessionStorage.setItem('information', JSON.stringify(info)); }); // 需要延迟以确保fetch已完成 setTimeout(() => { let info = JSON.parse(sessionStorage.getItem('information')); console.log('您的来源是 ' + info.origin); }, 3000);
第一次运行时,它需要通过网络解析请求并将结果存储在缓存中。第二次应该直接从本地存储中提取。
让我们从简单地包装fetch函数的代码开始:
fetch('https://httpbin.org/get') .then(r => r.json()) .then(issues => { console.log('您的来源是 ' + info.origin); });
这可以工作,但当然没用。让我们首先实现存储提取的数据。
cachedFetch('https://httpbin.org/get') .then(r => r.json()) .then(info => { console.log('您的来源是 ' + info.origin); });
这里有很多事情要做。
fetch返回的第一个Promise实际上会继续执行GET请求。如果CORS(跨源资源共享)有问题,.text()、.json()或.blob()方法将无法工作。
最有趣的功能是,我们必须克隆第一个Promise返回的Response对象。如果我们不这样做,我们就会过度注入自己,当Promise的最终用户尝试调用.json()(例如)时,他们会收到此错误:
const cachedFetch = (url, options) => { return fetch(url, options); };
需要注意的另一件事是对响应类型的仔细处理:我们只在状态码为200 并且内容类型为application/json或text/*时才存储响应。这是因为sessionStorage只能存储文本。
以下是如何使用它的示例:
const cachedFetch = (url, options) => { // 使用URL作为sessionStorage的缓存键 let cacheKey = url; return fetch(url, options).then(response => { // 让我们只在内容类型为JSON或非二进制内容时存储在缓存中 let ct = response.headers.get('Content-Type'); if (ct && (ct.match(/application\/json/i) || ct.match(/text\//i))) { // 有一个.json()而不是.text(),但我们将它存储在sessionStorage中作为字符串。 // 如果我们不克隆响应,它将在返回时被使用。这样我们就可以不干扰。 response.clone().text().then(content => { sessionStorage.setItem(cacheKey, content); }); } return response; }); };
到目前为止,这个解决方案的巧妙之处在于它可以工作,而且不会干扰JSON和HTML请求。当它是图像时,它不会尝试将其存储在sessionStorage中。
第二次实现 – 实际返回缓存命中
因此,我们的第一次实现只是负责存储请求的响应。但是,如果您第二次调用cachedFetch,它仍然不会尝试从sessionStorage检索任何内容。我们需要做的首先是返回一个Promise,并且Promise需要解析一个Response对象。
让我们从一个非常基本的实现开始:
<code>TypeError: Body has already been consumed.</code>
它可以工作!
要查看它的实际效果,请打开此代码的CodePen,然后在开发者工具中打开浏览器的“网络”选项卡。按几次“运行”按钮(CodePen的右上角),您应该会看到只有图像正在重复通过网络请求。
此解决方案的一个巧妙之处在于缺乏“回调意大利面”。由于sessionStorage.getItem调用是同步的(即阻塞的),我们不必在Promise或回调中处理“它是否在本地存储中?”。并且只有在有内容的情况下,我们才会返回缓存的结果。如果没有,if语句只会继续执行常规代码。
第三次实现 – 过期时间呢?
到目前为止,我们一直在使用sessionStorage,它就像localStorage一样,只是sessionStorage在您启动新选项卡时会被清除。这意味着我们正在利用一种“自然方式”来避免缓存时间过长。如果我们改用localStorage并缓存某些内容,即使远程内容已更改,它也会永远卡在那里,这很糟糕。
更好的解决方案是让用户控制。(在这种情况下,用户是使用我们的cachedFetch函数的Web开发人员)。就像服务器端的Memcached或Redis存储一样,您可以设置一个生存期,指定应缓存多长时间。
例如,在Python(使用Flask)中:
let origin = null; fetch('https://httpbin.org/get') .then(r => r.json()) .then(information => { origin = information.origin; // 您的客户端IP }); // 需要延迟以确保fetch已完成 setTimeout(() => { console.log('您的来源是 ' + origin); }, 3000);
现在,sessionStorage和localStorage都没有内置此功能,因此我们必须手动实现它。我们将通过始终记录存储时间的时间戳来做到这一点,并使用它来比较可能的缓存命中。
但在我们这样做之前,它会是什么样子?比如这样:
fetch('https://httpbin.org/get') .then(r => r.json()) .then(info => { sessionStorage.setItem('information', JSON.stringify(info)); }); // 需要延迟以确保fetch已完成 setTimeout(() => { let info = JSON.parse(sessionStorage.getItem('information')); console.log('您的来源是 ' + info.origin); }, 3000);
我们将添加的关键新内容是,每次保存响应数据时,我们也会记录何时存储它。但请注意,现在我们也可以切换到localStorage的更可靠存储,而不是sessionStorage。我们的自定义过期代码将确保我们不会在持久性localStorage中获得非常陈旧的缓存命中。
所以这是我们最终的工作解决方案:
fetch('https://httpbin.org/get') .then(r => r.json()) .then(issues => { console.log('您的来源是 ' + info.origin); });
未来的实现 – 更好、更花哨、更酷
我们不仅避免了过度访问这些Web API,最好的部分是localStorage比依赖网络快得多。请参阅这篇博文以了解localStorage与XHR的比较:localForage vs. XHR。它测量其他内容,但基本上得出结论,localStorage非常快,磁盘缓存预热很少见。
那么我们如何进一步改进我们的解决方案呢?
处理二进制响应
我们这里的实现不会缓存非文本内容(如图像),但没有理由不能缓存。我们需要更多代码。特别是,我们可能想要存储更多关于Blob的信息。每个响应基本上都是一个Blob。对于文本和JSON,它只是一个字符串数组。类型和大小并不重要,因为您可以从字符串本身推断出来。对于二进制内容,blob必须转换为ArrayBuffer。
对于好奇的人,要查看支持图像的实现扩展,请查看此CodePen:[https://www.php.cn/link/946af3555203afdb63e571b873e419f6]。
使用哈希缓存键
另一个潜在的改进是通过对每个URL(我们用作键)进行哈希处理来用空间换取速度,使其变得更小。在上面的示例中,我们只使用了一些非常小巧简洁的URL(例如https://httpbin.org/get),但是如果您有非常长的URL,有很多查询字符串内容,并且有很多这样的URL,那么它们加起来就会非常多。
解决这个问题的方法是使用这种巧妙的算法,它被认为是安全且快速的:
let origin = null; fetch('https://httpbin.org/get') .then(r => r.json()) .then(information => { origin = information.origin; // 您的客户端IP }); // 需要延迟以确保fetch已完成 setTimeout(() => { console.log('您的来源是 ' + origin); }, 3000);
如果您喜欢这个,请查看此CodePen:[https://www.php.cn/link/946af3555203afdb63e571b873e419f6]。如果您在Web控制台中检查存储,您会看到类似于557027443的键。
结论
您现在有一个可以添加到Web应用程序中的工作解决方案,在该解决方案中,您可能正在使用Web API,并且您知道响应可以很好地为您的用户缓存。
最后一件事可能是此原型的自然扩展,即将其超越文章,进入一个真实的、具体的项目,带有测试和自述文件,并在npm上发布它——但这留待以后再说!
关于缓存已提取AJAX请求的常见问题解答 (FAQ)
缓存已提取AJAX请求的重要性是什么?
缓存已提取的AJAX请求对于提高Web应用程序的性能至关重要。它允许浏览器存储服务器响应的副本,以便它不必再次发出相同的请求。这减少了服务器的负载,并加快了网页的加载时间,从而提供了更好的用户体验。
Fetch API如何与缓存一起工作?
Fetch API提供了一种强大且灵活的方法来发出HTTP请求。它包含一个内置的缓存机制,允许您指定请求应如何与缓存交互。您可以将缓存模式设置为“default”、“no-store”、“reload”、“no-cache”、“force-cache”或“only-if-cached”,每种模式都提供不同级别的缓存控制。
Fetch API中有哪些不同的缓存模式,它们是什么意思?
Fetch API提供了几种缓存模式。“default”遵循标准的HTTP缓存规则。“no-store”完全绕过缓存。“reload”忽略任何缓存数据并发送新的请求。“no-cache”在使用缓存版本之前使用服务器验证数据。“force-cache”无论其新鲜度如何都使用缓存数据。“only-if-cached”仅在缓存数据可用时才使用它,否则失败。
如何在AJAX请求中实现缓存?
您可以通过在AJAX设置中设置cache属性来在AJAX请求中实现缓存。如果设置为true,它将允许浏览器缓存响应。或者,您可以使用Fetch API的缓存选项来更好地控制缓存的行为。
如何防止AJAX请求中的缓存?
要防止AJAX请求中的缓存,您可以将AJAX设置中的cache属性设置为false。这将强制浏览器不将其响应存储在其缓存中。或者,您可以使用Fetch API的“no-store”缓存选项来完全绕过缓存。
AJAX和Fetch API中的缓存有什么区别?
虽然AJAX和Fetch API都提供了缓存机制,但Fetch API提供了更大的灵活性和控制性。AJAX的cache属性是一个简单的布尔值,它允许或不允许缓存。另一方面,Fetch API的缓存选项允许您指定请求应如何与缓存交互,从而为您提供更细粒度的控制。
缓存如何影响我的Web应用程序的性能?
缓存可以显著提高Web应用程序的性能。通过存储服务器响应的副本,浏览器不必再次发出相同的请求。这减少了服务器的负载,并加快了网页的加载时间。但是,必须正确管理缓存,以确保您的用户看到最新的内容。
我可以控制单个AJAX请求的缓存行为吗?
是的,您可以通过为每个请求在AJAX设置中设置cache属性来控制单个AJAX请求的缓存行为。这允许您指定浏览器是否应该缓存响应。
如何清除AJAX请求的缓存?
清除AJAX请求的缓存可以通过在AJAX设置中将cache属性设置为false来完成。这将强制浏览器不将其响应存储在其缓存中。或者,您可以使用Fetch API的“reload”缓存选项来忽略任何缓存数据并发送新的请求。
缓存AJAX请求的一些最佳实践是什么?
缓存AJAX请求的一些最佳实践包括:了解不同的缓存模式以及何时使用它们,正确管理缓存以确保用户看到最新的内容,以及使用Fetch API的缓存选项来更好地控制缓存。在决定缓存策略时,还必须考虑数据的性质和用户体验。
以上是缓存在本地提取Ajax请求:包装Fetch API的详细内容。更多信息请关注PHP中文网其他相关文章!

JavaScript字符串替换方法详解及常见问题解答 本文将探讨两种在JavaScript中替换字符串字符的方法:在JavaScript代码内部替换和在网页HTML内部替换。 在JavaScript代码内部替换字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 该方法仅替换第一个匹配项。要替换所有匹配项,需使用正则表达式并添加全局标志g: str = str.replace(/fi

利用轻松的网页布局:8个基本插件 jQuery大大简化了网页布局。 本文重点介绍了简化该过程的八个功能强大的JQuery插件,对于手动网站创建特别有用

因此,在这里,您准备好了解所有称为Ajax的东西。但是,到底是什么? AJAX一词是指用于创建动态,交互式Web内容的一系列宽松的技术。 Ajax一词,最初由Jesse J创造

该帖子编写了有用的作弊表,参考指南,快速食谱以及用于Android,BlackBerry和iPhone应用程序开发的代码片段。 没有开发人员应该没有他们! 触摸手势参考指南(PDF) Desig的宝贵资源

jQuery是一个很棒的JavaScript框架。但是,与任何图书馆一样,有时有必要在引擎盖下发现发生了什么。也许是因为您正在追踪一个错误,或者只是对jQuery如何实现特定UI感到好奇

10款趣味横生的jQuery游戏插件,让您的网站更具吸引力,提升用户粘性!虽然Flash仍然是开发休闲网页游戏的最佳软件,但jQuery也能创造出令人惊喜的效果,虽然无法与纯动作Flash游戏媲美,但在某些情况下,您也能在浏览器中获得意想不到的乐趣。 jQuery井字棋游戏 游戏编程的“Hello world”,现在有了jQuery版本。 源码 jQuery疯狂填词游戏 这是一个填空游戏,由于不知道单词的上下文,可能会产生一些古怪的结果。 源码 jQuery扫雷游戏

本教程演示了如何使用jQuery创建迷人的视差背景效果。 我们将构建一个带有分层图像的标题横幅,从而创造出令人惊叹的视觉深度。 更新的插件可与JQuery 1.6.4及更高版本一起使用。 下载


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

螳螂BT
Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

Atom编辑器mac版下载
最流行的的开源编辑器

Dreamweaver Mac版
视觉化网页开发工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3 英文版
推荐:为Win版本,支持代码提示!