目录
主要收获
Fetch API
简单的替代方案
第一次实现 – 保持简单
第二次实现 – 实际返回缓存命中
第三次实现 – 过期时间呢?
未来的实现 – 更好、更花哨、更酷
处理二进制响应
使用哈希缓存键
结论
关于缓存已提取AJAX请求的常见问题解答 (FAQ)
缓存已提取AJAX请求的重要性是什么?
Fetch API如何与缓存一起工作?
Fetch API中有哪些不同的缓存模式,它们是什么意思?
如何在AJAX请求中实现缓存?
如何防止AJAX请求中的缓存?
AJAX和Fetch API中的缓存有什么区别?
缓存如何影响我的Web应用程序的性能?
我可以控制单个AJAX请求的缓存行为吗?
如何清除AJAX请求的缓存?
缓存AJAX请求的一些最佳实践是什么?
首页 web前端 js教程 缓存在本地提取Ajax请求:包装Fetch API

缓存在本地提取Ajax请求:包装Fetch API

Feb 17, 2025 am 11:06 AM

Cache Fetched AJAX Requests Locally: Wrapping the Fetch API

本文由特邀作者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);
登录后复制
登录后复制
登录后复制
登录后复制

CodePen示例

这仅仅依赖于全局变量来保存缓存的数据。直接的问题是,如果您重新加载页面或导航到新页面,缓存的数据就会消失。

在我们剖析其缺点之前,让我们升级一下第一个简单的解决方案。

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);
登录后复制
登录后复制
登录后复制

CodePen示例

第一个直接的问题是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);
登录后复制
登录后复制
登录后复制
登录后复制

CodePen示例

现在您想对其进行包装,以便重复的网络调用可以从本地缓存中获益。让我们简单地将其称为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);
  });
登录后复制
登录后复制

CodePen示例

这可以工作,但当然没用。让我们首先实现存储提取的数据。

cachedFetch('https://httpbin.org/get')
  .then(r => r.json())
  .then(info => {
    console.log('您的来源是 ' + info.origin);
  });
登录后复制

CodePen示例

这里有很多事情要做。

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;
  });
};
登录后复制

到目前为止,这个解决方案的巧妙之处在于它可以工作,而且不会干扰JSONHTML请求。当它是图像时,它不会尝试将其存储在sessionStorage中。

第二次实现 – 实际返回缓存命中

因此,我们的第一次实现只是负责存储请求的响应。但是,如果您第二次调用cachedFetch,它仍然不会尝试从sessionStorage检索任何内容。我们需要做的首先是返回一个Promise,并且Promise需要解析一个Response对象。

让我们从一个非常基本的实现开始:

<code>TypeError: Body has already been consumed.</code>
登录后复制

CodePen示例

它可以工作!

要查看它的实际效果,请打开此代码的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 =&gt; r.json())
  .then(information =&gt; {
    origin = information.origin; // 您的客户端IP
  });

// 需要延迟以确保fetch已完成
setTimeout(() =&gt; {
  console.log('您的来源是 ' + origin);
}, 3000);
登录后复制
登录后复制
登录后复制
登录后复制

现在,sessionStorage和localStorage都没有内置此功能,因此我们必须手动实现它。我们将通过始终记录存储时间的时间戳来做到这一点,并使用它来比较可能的缓存命中。

但在我们这样做之前,它会是什么样子?比如这样:

fetch('https://httpbin.org/get')
  .then(r =&gt; r.json())
  .then(info =&gt; {
    sessionStorage.setItem('information', JSON.stringify(info));
  });

// 需要延迟以确保fetch已完成
setTimeout(() =&gt; {
  let info = JSON.parse(sessionStorage.getItem('information'));
  console.log('您的来源是 ' + info.origin);
}, 3000);
登录后复制
登录后复制
登录后复制

我们将添加的关键新内容是,每次保存响应数据时,我们会记录何时存储它。但请注意,现在我们也可以切换到localStorage的更可靠存储,而不是sessionStorage。我们的自定义过期代码将确保我们不会在持久性localStorage中获得非常陈旧的缓存命中。

所以这是我们最终的工作解决方案:

fetch('https://httpbin.org/get')
  .then(r =&gt; r.json())
  .then(issues =&gt; {
    console.log('您的来源是 ' + info.origin);
  });
登录后复制
登录后复制

CodePen示例

未来的实现 – 更好、更花哨、更酷

我们不仅避免了过度访问这些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 =&gt; r.json())
  .then(information =&gt; {
    origin = information.origin; // 您的客户端IP
  });

// 需要延迟以确保fetch已完成
setTimeout(() =&gt; {
  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中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门文章

两个点博物馆:邦格荒地地点指南
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热门工具标签

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

在JavaScript中替换字符串字符 在JavaScript中替换字符串字符 Mar 11, 2025 am 12:07 AM

在JavaScript中替换字符串字符

jQuery检查日期是否有效 jQuery检查日期是否有效 Mar 01, 2025 am 08:51 AM

jQuery检查日期是否有效

jQuery获取元素填充/保证金 jQuery获取元素填充/保证金 Mar 01, 2025 am 08:53 AM

jQuery获取元素填充/保证金

前5个日期操纵JS插件 前5个日期操纵JS插件 Feb 28, 2025 am 12:34 AM

前5个日期操纵JS插件

10值得检查jQuery插件 10值得检查jQuery插件 Mar 01, 2025 am 01:29 AM

10值得检查jQuery插件

10个jQuery手风琴选项卡 10个jQuery手风琴选项卡 Mar 01, 2025 am 01:34 AM

10个jQuery手风琴选项卡

jQuery添加卷轴到Div jQuery添加卷轴到Div Mar 01, 2025 am 01:30 AM

jQuery添加卷轴到Div

自定义Google搜索API设置教程 自定义Google搜索API设置教程 Mar 04, 2025 am 01:06 AM

自定义Google搜索API设置教程

See all articles