Appearance
缓存
浏览器缓存是允许将用户之前请求过的资源(如 HTML 页面、图片、脚本等)存储在本地,当再次访问相同资源时,可以直接从本地读取而不是重新向服务器发起请求。可以减少请求和性能优化,提升用户体验。
http 缓存是浏览器或代理服务器对之前请求过的资源进行存储,当再次请求相同资源时,可以直接从本地缓存中获取,避免重复向服务器发起请求的机制,,可显著减少网络传输量、降低服务器负载、提升用户体验。
若缓存生效,强缓存返回状态码200
,协商缓存返回状态码304
。
核心流程:
- 浏览器向服务器请求资源时,服务器返回资源及缓存控制头(如 Cache-Control、Expires),浏览器将资源存储到本地缓存。
- 浏览器再次请求相同资源时,会先检查本地缓存,如果缓存未过期,则直接从缓存中获取资源,而不再向服务器发起请求。
强缓存
服务端给响应头追加一些字段(expires),在 expires 截止失效时间之前,无论如何刷新页面,都不会重新请求
Expires
: 服务器返回一个时间,由客户端时间决定是否失效
js
// 响应头添加
ctx.set('Expires', new Date(Date.now() + 10 * 1000).toUTCString())
Cache-Control: max-age=86400
: 最大缓存时间,在 max-age 截止失效时间之前,只要客户端发送请求,服务端不会返回新的资源,客户端会直接使用本地缓存
js
// 响应头添加
ctx.set('Cache-Control', 'public, max - age=10')
协商缓存
当服务端标记上协商缓存后,客户端再下次请求需要发送请求到服务器,由服务器判断是否需要缓存获取(主要是去判断文件内容是否变化)
协商缓存(两组)
第一组:
- 响应状态码:
Last-Modified(最后修改时间)
初次加载时,服务器返回一个时间
Last-Modified
给客户端,客户端下次请求时会带上这个时间值(If-Modified-Since
),单位是秒,如果在1秒内再次请求,就算是文件修改了,也不会重新请求资源。- 请求状态码:
If-Modified-Since
第一次响应后,客户端发送请求时,会在
If-Modified-Since
携带Last-Modified
的值,后端去判断是否改变,变了就返回最新数据,没变就返回状态码304
,走缓存js// 服务器 async lastModifiedTest(ctx) { // 模拟资源数据 const resource = { id: 1, name: 'Example Resource' }; // 模拟最后修改时间 const lastModifiedTime = 1689253117727; // 检查客户端发送的 if-modified-since const ifModifiedSince = ctx.get('If-Modified-Since'); if (ifModifiedSince && new Date(ifModifiedSince).getTime() > lastModifiedTime) { // 如果请求的 If-Modified-Since 大于最后修改时间,则返回缓存状态码 ctx.status = 304; return; } // 模拟3秒请求 await new Promise(resolve => { setTimeout(() => { resolve(); }, 3000); }); // 设置响应头中的 ETag ctx.set('Last-Modified', new Date(lastModifiedTime).toUTCString()); this.success(ctx, resource); }
- 响应状态码:
第二组:(升级)
- 响应状态码:
Etag(文件hash)
文件 hash 值赋给
Etag
,服务端响应的是Etag
- 请求状态码:
If-None-Match
去判断
Etag
是否改变,没变就返回状态码304
,走缓存ETag的值是根据资源内容生成的,通常是资源的 MD5 哈希值。你可以使用 Node.js 的
crypto
模块来生成 ETag 值。(会增加服务器开销)js// 服务器 async etagTest(ctx) { // 模拟资源数据 const resource = { id: 1, name: 'Example Resource' }; // 生成 ETag const resourceETag = md5(`${resource.id}${resource.name}`); // 检查客户端发送的 If-None-Match 头 const requestETag = ctx.get('If-None-Match'); if (requestETag === resourceETag) { // 如果请求的 ETag 与资源的 ETag 匹配,则返回缓存状态码 ctx.status = 304; return; } // 模拟3秒请求数据 await new Promise(resolve => { setTimeout(() => { resolve(); }, 3000); }); // 设置响应头中的 ETag ctx.set('ETag', resourceETag); this.success(ctx, resource); }
- 响应状态码:
策略缓存
所用技术 service worker
策略缓存可以拦截前端资源请求,并约定请求缓存策略(一般有 4 种)
文档:https://developer.chrome.com/docs/workbox/caching-strategies-overview?hl=zh_cn#caching_strategies
仅限缓存
:仅缓存,不请求网络仅限网络
:仅请求网络,不缓存先缓存,然后回退到网络
:(一般常用)- 请求命中缓存。如果资源在缓存中,请从缓存中提供。
- 如果请求不在缓存中,请转到网络。
- 在网络请求完成后,将其添加到缓存中, 然后从网络返回响应。
先网络,回退到缓存
:- 您首先要访问网络以获取请求,然后将响应放入缓存中。
- 如果您稍后处于离线状态, 则您会在缓存中回退到该响应的最新版本。