目录
主要收获
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 => 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);
  });
登录后复制
登录后复制

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 => 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中文网其他相关文章!

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

热AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

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

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

前端热敏纸小票打印遇到乱码问题怎么办? 前端热敏纸小票打印遇到乱码问题怎么办? Apr 04, 2025 pm 02:42 PM

前端热敏纸小票打印的常见问题与解决方案在前端开发中,小票打印是一个常见的需求。然而,很多开发者在实...

神秘的JavaScript:它的作用以及为什么重要 神秘的JavaScript:它的作用以及为什么重要 Apr 09, 2025 am 12:07 AM

JavaScript是现代Web开发的基石,它的主要功能包括事件驱动编程、动态内容生成和异步编程。1)事件驱动编程允许网页根据用户操作动态变化。2)动态内容生成使得页面内容可以根据条件调整。3)异步编程确保用户界面不被阻塞。JavaScript广泛应用于网页交互、单页面应用和服务器端开发,极大地提升了用户体验和跨平台开发的灵活性。

谁得到更多的Python或JavaScript? 谁得到更多的Python或JavaScript? Apr 04, 2025 am 12:09 AM

Python和JavaScript开发者的薪资没有绝对的高低,具体取决于技能和行业需求。1.Python在数据科学和机器学习领域可能薪资更高。2.JavaScript在前端和全栈开发中需求大,薪资也可观。3.影响因素包括经验、地理位置、公司规模和特定技能。

如何使用JavaScript将具有相同ID的数组元素合并到一个对象中? 如何使用JavaScript将具有相同ID的数组元素合并到一个对象中? Apr 04, 2025 pm 05:09 PM

如何在JavaScript中将具有相同ID的数组元素合并到一个对象中?在处理数据时,我们常常会遇到需要将具有相同ID�...

JavaScript难以学习吗? JavaScript难以学习吗? Apr 03, 2025 am 12:20 AM

学习JavaScript不难,但有挑战。1)理解基础概念如变量、数据类型、函数等。2)掌握异步编程,通过事件循环实现。3)使用DOM操作和Promise处理异步请求。4)避免常见错误,使用调试技巧。5)优化性能,遵循最佳实践。

如何实现视差滚动和元素动画效果,像资生堂官网那样?
或者:
怎样才能像资生堂官网一样,实现页面滚动伴随的动画效果? 如何实现视差滚动和元素动画效果,像资生堂官网那样? 或者: 怎样才能像资生堂官网一样,实现页面滚动伴随的动画效果? Apr 04, 2025 pm 05:36 PM

实现视差滚动和元素动画效果的探讨本文将探讨如何实现类似资生堂官网(https://www.shiseido.co.jp/sb/wonderland/)中�...

JavaScript的演变:当前的趋势和未来前景 JavaScript的演变:当前的趋势和未来前景 Apr 10, 2025 am 09:33 AM

JavaScript的最新趋势包括TypeScript的崛起、现代框架和库的流行以及WebAssembly的应用。未来前景涵盖更强大的类型系统、服务器端JavaScript的发展、人工智能和机器学习的扩展以及物联网和边缘计算的潜力。

console.log输出结果差异:两次调用为何不同? console.log输出结果差异:两次调用为何不同? Apr 04, 2025 pm 05:12 PM

深入探讨console.log输出差异的根源本文将分析一段代码中console.log函数输出结果的差异,并解释其背后的原因。�...

See all articles