前端性能优化之HTTP缓存策略

背景

很多时候,当打开浏览器的开发者工具,查看网络请求,对于资源大小(Size)选项,除了有具体的数字大小,还有from memory cache、from disk cache字段之类出现。

这里就有很多疑问,这些字段代表着什么意思?这些字段又是谁来决定的?

image

缓存位置

从字面意思理解,大概也能猜到,这些字段代表着缓存位置。
按优先级,Size选项字段可分为:

  • from Service Worker
  • from memory cache
  • from disk cache
  • 真正的网络请求(显示资源的具体大小)

Service Worker

本质是作为服务器与客户端之间的代理服务器,伴随着PWA出现。Service Worker真正意义上将缓存控制权交给了前端,相比于LocalStorage、SessionStorage,后两者只是单纯的接口数据缓存,例如用户信息(一个对象)、列表信息(一个数组),而前者可以缓存静态资源,甚至拦截网络请求,根据网络状况作出不同的缓存策略。当然,这不是本文讨论的重点。

memory cache

顾名思义,这个是将资源缓存在了内存中。事实上,所有的网络请求都会被浏览器缓存到内存中,当然,内存容量有限,缓存不能无限存放在内存中,因此,注定是个短期缓存。

内存缓存的控制权在浏览器,前后端都不能干涉。

disk cache

与内存缓存相对的,这个是将资源缓存在硬盘中。虽然相比于内存,硬盘的读取速度要慢很多,但总比没有强。

硬盘缓存的控制权在后端,通过什么控制呢?通过HTTP响应头控制,这是本文重点讨论的。

缓存策略

disk cache也叫http cahce,因为其严格遵守http响应头字段来判断哪些资源是否要被缓存,哪些资源是否已经过期。绝大多数缓存都是disk cache。

disk cahce分为强制缓存与对比缓存。

强制缓存

控制强制缓存的有两种http响应头字段:

Expires: Fri, 08 Feb 2019 05:37:33 GMT

字段的值就代表了资源的过期时间,不过这个值是相对于客户端,并且客户端本地时间可以任意修改,因此这个字段并不可靠。Expires字段是Http 1.0的,Http 1.1 用Cache-Control字段替代它:

Cache-Control: max-age=2592000

Cache-control字段使用了绝对时间,单位为秒,即最大有效时间,在有效时间内,客户端直接从硬盘中读取资源。

看个例子,用Node.js搭建一个静态资源服务器,设置Cache-Control: max-age=2592000,每次请求都会被服务器打印出:

const server = http.createServer((req, res) => {
    console.log(`收到请求,请求地址为: ${req.url}`);
    fs.readFile(path.resolve(__dirname, './image.png'), (err, file) => {
        if (err) {
            res.end(err.message);
        }

        res.setHeader('Cache-control', 'max-age=2592000');
        res.end(file);
    });
}).listen(3000);

console.log('localhost:3000服务已开启!');
image

第一次访问:

image
image

第二次访问:

image
image

可以看到,第一次请求,浏览器根据响应头中的Cache-Control字段,将资源缓存在硬盘中,第二次请求,浏览器直接从硬盘中读取资源,并没有发送网络请求到服务器。

Cache-Control字段有以下可取值:

  • max-age=xxx,最大的有效时间
  • must-revalidate,如果超过了max-age的时间,必须向服务器发送请求,验证资源的有效性
  • no-cache,基本等价于max-age=0,由对比缓存来决定是否缓存资源
  • no-store,真正意义上的不缓存
  • public,所有内容都可以被缓存
  • private,所有内容只有客户端可以缓存,代理服务器不能缓存。默认值

对比缓存

不同于强制缓存,浏览器直接根据响应头Cache-Control字段直接判断缓存资源是否有效,对比缓存需要再次向服务器确认。

Last-Modified & If-Modified-Since

服务器通过响应头Last-Modified告知浏览器,资源最后被修改的时间:

Last-Modified: Fri, 08 Feb 2019 15:20:04 GMT

当再次请求该资源时,浏览器需要再次向服务器确认,资源是否过期,其中的凭证就是请求头If-Modified-Since字段,值为上次请求中响应头Last-Modified字段的值:

If-Modified-Since: Fri, 08 Feb 2019 15:20:04 GMT

服务器会接收If-Modified-Since字段的值与被请求资源的最后修改时间作比较

如果If-Modified-Since的值大于被请求资源的最后修改时间,则说明浏览器缓存的资源仍然有效,服务器会返回304状态码,告知浏览器直接取缓存即可。其中服务器返回的只有Http头部,并不包含主体(不然就没有缓存的意义了)。

否则,就跟正常的请求一样,服务器返回200状态码,并附带最新的资源。

看个例子,稍微修改下刚才的Node.js代码:

const server = http.createServer((req, res) => {
    console.log(`收到请求,请求地址为: ${req.url}`);

    const filename = path.resolve(__dirname, './image.png');

    fs.stat(filename, (err, stat) => {
        const lastModified = stat.mtime.toUTCString();

        if (lastModified === req.headers['if-modified-since']) {
            res.writeHead(304, 'Not Modified');
            res.end();
        }
        else {
            fs.readFile(filename, (err, file) => {
                if (err) {
                    res.end(err.message);
                }
                
                res.setHeader('Last-Modified', lastModified);
                res.end(file);
            });
        }
    });
}).listen(3000);

console.log('localhost:3000服务已开启!');

第一次请求:

image

第二次请求:

image

比对两次请求可以看到,除了状态码变成了304,资源大小也从57.8K降到了90B,这也证明响应中不包含http主体。

Etag & If-None-Match

Last-Modiflied与Expires一样,也是有缺陷的。如果,资源的变化的时间间隔小于秒级,比如说是毫秒级的,或者说资源直接是动态生成的,那根据Last-Modified判断,资源就是每时每刻都最新的,即被修改过!

所以,Etag & If-Node-Match 就是来解决这个问题的。

Etag字段的值为文件的特殊标识,一般都是hash生成的,服务器存储着资源的Etag值。接下来的流程都与Lst-Modified & If-Modified-Since一致,只不过,比较的值从最后修改时间变成了Etag值。

Etag的优点在于,对于动态资源或者现在流行的Restful API返回的JSON数据,这些是没有修改时间这一说法的,但是Http标准并没有规定Etag值如何生成,因此我们通过代码自己生成Etag值。当然,计算Etag值会消耗服务器性能。

优先级

强制缓存与对比缓存是可以同时存在的,并且强制缓存的优先级高于对比缓存。实际应用中,也是两者共同使用的。

看个例子,在响应头中同时加上Cache-Control与Last-Modified:

res.setHeader('Cache-control', 'max-age=600');
res.setHeader('Last-Modified', lastModified);

第一次请求:

image

第二次请求:

image

可以看到,虽然有Last-Modified字段,但还是直接从硬盘中获取资源。

总结

Http缓存策略,其实只是前端缓存的一小部分,但零乱的知识点还是非常多的。最终处理缓存还是浏览器,各浏览器的处理方式可能有差异,实际应用中还是要慎重考虑。

合理运用Http缓存,对前端性能优化还是非常有帮助的!

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

推荐阅读更多精彩内容

  • 网络特有的延迟以及数据传输的成本,制约互联网快速获取Web资源。为此,HTTP协议引入缓存以空间换时间,使浏览器缓...
    大头8086阅读 3,052评论 2 12
  • 0 前言 在前端开发中,性能一直都是被大家所重视的一点,然而判断一个网站的性能最直观的就是看网页打开的速度。其中提...
    七寸知架构阅读 8,943评论 7 58
  • 本文内容大多参考《图解HTTP》一书 一. 认识代理服务器 所以讲缓存为什么要先扯代理服务器?别急,让我们看一下一...
    流光号船长阅读 1,894评论 0 10
  • 浏览器对于请求资源, 流程如图所示: 可以看到浏览器的缓存机制分为两个部分: 1、当前缓存是否过期? 2、服务器中...
    zhoulujun阅读 1,168评论 0 3
  • 1.修行目标:我要过内圣外王的生活、轻松成功、健康丰盛,喜乐,拥有氧气般的金钱,全家每月总收入达5万,成为成功自由...
    颖仪_5b92阅读 92评论 0 0