注册

【白话前端】从一个故事说明白“浏览器缓存”

一则小故事



小明常去图书馆借阅英文杂志回家看,由于单词量少,他同时需要借阅一本《英汉词典》;




起初,和图书管理员不熟,每次他都要在图书馆借英文杂志和《英汉词典》,放在书包里背回家;这个过程,暂且将其称为“不缓存”;




后来,小明发现图书管理员竟是妈妈的好朋友和好邻居王叔叔,经过相认后,王叔叔对小明说:“你每次都要借阅《英汉词典》,我直接借你一整年,在一年内你可以将它放在家里,不需要每次到图书馆来借阅。”小明听了非常高兴,因为他的书包可以轻上一大截;可以持有《英汉词典》一整年的过程,暂且称为“强缓存”;




再后来,小明发现图书管理员王叔叔经常去家里做客,两人关系也愈发亲密;小明问:“王叔叔,英文杂志的更新总是很不规律,我经常去了图书馆,英文杂志却未更新,我借到的依然是上一期的杂志,有啥办法让我少跑路吗?”




王叔叔笑着说:“这还不简单?每次你准备去借阅之前,先把你手里当前持有的杂志期号(etag)用短信发给我,如果图书馆没有更新,我就给你一个304的暗号,你就还是接着读家里那本;如果有了更新,我给你一个200的暗号,你再来图书馆拿书就行;”这个过程,暂且被称为“协商缓存”



逐渐装逼


不缓存


不缓存是最容易理解的缓存策略,也最不容易出错,只要每一次刷新页面都去服务器取数据即可;但同样的,不缓存意味着页面加载速度会更慢


要设置不缓存也很容易,只需要将资源文件的Response Header中的Cache-Control设为no-store即可;



Cache-Control: no-store



cache-control 属性之一:可缓存性

83d7bcd73031cece2ccad14819781cbc.png


强缓存


对于已知的几乎不会发生变化的资源,可以通过调整策略,使得浏览器在限定时间内,直接从本地缓存获取,这就是所谓的强缓存;

要配置静态资源的强缓存,通常需要发送的缓存头如下:



Cache-Control:public, max-age=31536000



以下是强缓存常用的两种属性↓;


cache-control 属性之:可缓存性

9b680579d622bdf64453c5a9459ebf2f.png

cache-control 属性之: 到期

2c41424ab2062b9c4d0d495011d9db15.png

协商缓存


其实上面故事里关于协商缓存的描述,有一点是非常不准确的,那就是对于浏览器而言,小明发送给王叔叔的不是所谓的“杂志期号”,而是杂志的散列(hash);而这个hash,自然也是王叔叔(服务器端)告诉小明(客户端)的;


在真实情况下,浏览器的协商缓存要触发,只有两种情况:



1.Cache-Control 的值为 no-cache (不强缓存)



or



2.max-age 过期了 (强缓存,但总有过期的时候)



只有在这两种情况下满足其中至少一种时,才会进入协商缓存的过程;


因此,常规的协商缓存,通常分为以下几步:



step1

浏览器第一次发起请求,request上并没有相应请求头;

(小明第一次去图书馆借书)




step2

服务器第一次返回资源,response上带上了两个属性:
etag: "33a64df"

last-modified: Mon, 12 Dec 2020 12:12:12 GMT

(王叔叔借给小明一本书,并告诉小明这本杂志的编号,以及它的发刊日期)




step3

浏览器第二次发起请求,request上携带了上一次请求返回的内容:

if-none-matched: "33a64df"

if-modified-since: Mon, 12 Dec 2020 12:12:12 GMT

(小明第二次借书,先进行了询问:上一次借我的那本的编号和上一次更改后是否有变动?)




step4

服务器发现资源没有改变,于是返回了304状态码;

浏览器直接在本地读取缓存;

(王叔叔说:还没来新货,你先读着上次借的那本吧)




step5

浏览器第三次发起请求,request上携带了上一次请求返回的内容:

if-none-matched: "33a64df"

if-modified-since: Mon, 12 Dec 2020 12:12:12 GMT

(小明第三次借书,先进行了询问:上一次借我的那本的编号和上一次更改后是否有变动?)




step6

服务器检查之后发现,文件已经发生了变化,于是将新的资源、编号、最后变更时间一起返回给了客户端;并返回了200状态码;
if-none-matched: "sd423dss"

if-modified-since: Mon, 30 Dec 2020 12:12:12 GMT

(王叔叔说:来了来了,最新一期的杂志编号、发刊日期如下,这是杂志本身,也一起给你;)



上面过程展示了一次协商缓存生效的过程;


如何在项目中使用?


正常来说,一个前端单页应用(SPA)的项目结构大概如下:


├─favicon.ico
├─index.html

├─css
│ └───app.fb0c6e1c.css

├─img
│ └───logo.82b9c7a5.png

└─js
├───app.febf7357.js
└───chunk-vendors.5a5a5781.js

从命名上可以发现,文件大概分两类:



  1. index.html & favicon.ico 都属于固定命名,通常情况下名称不会再发生改变;
  2. css/js/image/ttf 等文件,则通常会以 {name}.{hash}.{suffix}的方式进行命名;

name-with-hash.png


当文件发生变化时,其命名规则,可天然保证文件hash跟着发生变化,从而保证文件的路径发生变化;


因此,针对以上场景,通常情况下可以按以下方式制定缓存策略



  1. index.html 和 favicon.ico 设置为“不缓存”或者“协商缓存”(必要不大);
  2. 名称中带hash的文件(如css/js/image/ttf),可以直接使用“强缓存”策略

作者:春哥的梦想是摸鱼
链接:https://juejin.cn/post/7030781324650610695

0 个评论

要回复文章请先登录注册