前端缓存以及react-query(SWR)的简单实现

最近在阅读图解HTTP,看到了cache-control这块内容,之前的确关注很少。加上最近也正在使用react-query,也在疑惑他的缓存机制。刚好花时间研究了下前端缓存。

cache-control

  • max-age

首先http header是有cache-control这个属性, 当我们想使用缓存机制, 我们最常用的就是max-age。
例如我现在有一个请求a,请求a在http header设置的cache-control为max-age=10,那么浏览器就会根据这个请求a的url以及header和body等等作为你这个请求的唯一标识,存储在缓存中。当10s内你再次发送这个请求a,那么就会命中缓存,而不会去请求服务器。

此时,我是有一个疑问的,那么当请求a的max-age设置为10s,那么当我第5s的时候刷新浏览器,那么这个请求a是会取缓存还是会命中服务器取最新数据呢?
在刷新页面的情况下,浏览器默认会忽略缓存,并向后端服务器发送请求以获取最新的响应。这是因为刷新操作通常意味着用户希望获取最新的页面内容,而不是使用缓存的数据。

  • max-stale

我个人感觉max-stale使用场景不是很多,max-stale是处理缓存数据的,例如我设置了max-age=10 , max-stale=10,就代表发送一个请求后,我们这个缓存会存储10s,当缓存过期后,我们在过期后的10s内,我们还是会继续使用缓存。

  • no-cache && no-store

其中的no-cache并非是使浏览器不缓存文件,而是缓存文件却不使用缓存,强制浏览器向服务器获取资源。而no-store才是强制浏览器不缓存也不使用缓存。

no-cache也用于关闭强缓存

日常前端缓存实现

那么当我们知道了max-age和max-stale这些基础概念后,我们前端日常做缓存又是怎么做的呢?可能大家都知道强缓存/协商缓存的概念。

  • 强缓存

强缓存其实就是我们上面所讲的这些知识了,通过浏览器来处理, 我前端认为这个请求缓存10s就缓存10s。服务器就算更新数据了,前端也会不知道的。

  • 协商缓存

通用做法是后端会根据我们前端在请求头传给它某个标识去判断是否取缓存数据返回给前端。
http-header是提供了两个请求头标识,通用的两个请求头标识是If-Modified-Since / If-None-Match.

如果开启协商缓存,例如使用ETag, 当后端给ETag,set值后,过后的请求就会自动带上If-None-Match

image.png
  • If-Modified-Since
    它的值是上一次请求中服务器返回的响应头中的"Last-Modified"字段的值,传给后端,后端会去比较,如果变化则会返回304,告诉前端使用缓存。
  • If-None-Match
    它的值是上一次请求中服务器返回的响应头中的"ETag"字段的值, 传给后端,后端会去比较,如果变化则会返回304,告诉前端使用缓存。

当这个请求,后端判断不需要从缓存中取的时候,会返回200和去查数据返回给前端,当后端判断需要从缓存中取的时候,后端会返回304 http 状态码

当http status code 为 304的时候,浏览器会默认去取缓存的值,而不需要开发者额外的操作

image.png

image.png

react-query / SWR

普通的缓存策略是这样的,也就是我们使用协商缓存:当一个资源的缓存过期之后,我们请求后端,后端需要花时间去查询返回给我们新的数据,在这个期间,客户端就得等待,直到请求结束。

而这个 SWR 策略是说:当资源过期,进行重新请求时,客户端可以不等待,直接使用过期的缓存,请求完成后缓存就更新了,下次用的就是新的了。

我一直理解为react-query或者swr是不是利用和http-header,现在想来react-query和swr其实也是强缓存的一种形式,只是通过代码层面,帮我们完成了缓存,重复请求等相关问题,也更好的帮助我们管理了后端数据。

以下是简单实现

const cache = new Map();

async function swr(cacheKey, fetcher, cacheTime) {
  const data = cache.get(cacheKey) || { value: null, time: 0, promise: null };
  cache.set(cacheKey, data);

  const isStaled = Date.now() - data.time > cacheTime;
  if (isStaled && !data.promise) {
    data.promise = fetcher()
      .then((val) => {
        data.val = val;
        data.time = Date.now();
      })
      .catch((err) => {
        console.log(err);
      })
      .finally(() => {
        data.promise = null;
      });
  }

  if (data.promise && !data.value) await data.promise;

  return data.value;
}

以下是模拟协商缓存demo

server.js

const http = require("http");
const fs = require("fs");

const server = http.createServer((req, res) => {
  if (req.url === "/") {
    fs.readFile("./index.html", (err, data) => {
      if (err) {
        res.writeHead(404, { "Content-Type": "text/plain" });
        res.end("File not found");
      } else {
        res.writeHead(200, { "Content-Type": "text/html" });
        res.end(data);
      }
    });
  } else if (req.url === "/api/getUserInfo" && req.method === "GET") {
    const user = { name: "John Doe", age: 30 };
    res.setHeader("Cache-Control", "no-cache");
    const ifNoneMatch = req.headers["if-none-match"];
    if (ifNoneMatch === "liyu12") {
      res.setHeader("ETag", "liyu12");
      res.statusCode = 304;
      res.end();
    } else {
      res.setHeader("ETag", "liyu12");
      res.end(JSON.stringify(user));
    }
  }
});

server.listen(3333, () => {
  console.log("server listen on 3333");
});

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script>
    fetch('/api/getUserInfo').then(user => console.log('user', user))
</script>

<body>
    <div>Hello World</div>
</body>

</html>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,682评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,277评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,083评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,763评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,785评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,624评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,358评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,261评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,722评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,900评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,030评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,737评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,360评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,941评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,057评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,237评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,976评论 2 355

推荐阅读更多精彩内容