微信号:FrontDev

介绍:分享 Web 前端相关的技术文章、工具资源、精选课程、热点资讯

浏览器缓存知识归纳

2016-06-12 20:55 伯乐专栏/庞健

(点击上方公众号,可快速关注)

作者:伯乐在线专栏作者 - pangjian

链接:http://web.jobbole.com/86366/

点击 → 了解如何加入专栏作者


浏览器缓存(Browser Catching)是为了节约网络的资源加速浏览,浏览器在用户磁盘上对最近请求过的文档进行存储,当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样就可以加速页面的阅览。 —-摘自《百度百科》


浏览器缓存是提升网页性能的一大利器,但是,也是一把双刃剑。利用的好网页的性能会有大幅度提升,服务器的压力也会减小。利用的不好,也会遇到很多的问题。本文结合浏览器缓存的知识,结合真实案例进行分析,希望对读者有所帮助。


浏览器缓存分类


浏览器缓存分为强缓存和协商缓存,浏览器加载一个页面的简单流程如下:


  1. 浏览器先根据这个资源的http头信息来判断是否命中强缓存。如果命中则直接加在缓存中的资源,并不会将请求发送到服务器。

  2. 如果未命中强缓存,则浏览器会将资源加载请求发送到服务器。服务器来判断浏览器本地缓存是否失效。若可以使用,则服务器并不会返回资源信息,浏览器继续从缓存加载资源。

  3. 如果未命中协商缓存,则服务器会将完整的资源返回给浏览器,浏览器加载新资源,并更新缓存。


强缓存


命中强缓存时,浏览器并不会将请求发送给服务器。在Chrome的开发者工具中看到http的返回码是200,但是在Size列会显示为(from cache)。



强缓存是利用http的返回头中的Expires或者Cache-Control两个字段来控制的,用来表示资源的缓存时间。


Expires


该字段会返回一个时间,比如Expires:Thu,31 Dec 2037 23:59:59 GMT。这个时间代表着这个资源的失效时间,也就是说在2037年12月31日23点59分59秒之前都是有效的,即命中缓存。这种方式有一个明显的缺点,由于失效时间是一个绝对时间,所以当客户端本地时间被修改以后,服务器与客户端时间偏差变大以后,就会导致缓存混乱。于是发展出了Cache-Control。


Cache-Control


Cache-Control是一个相对时间,例如Cache-Control:3600,代表着资源的有效期是3600秒。由于是相对时间,并且都是与客户端时间比较,所以服务器与客户端时间偏差也不会导致问题。


Cache-Control与Expires可以在服务端配置同时启用或者启用任意一个,同时启用的时候Cache-Control优先级高。


协商缓存


若未命中强缓存,则浏览器会将请求发送至服务器。服务器根据http头信息中的Last-Modify/If-Modify-Since或Etag/If-None-Match来判断是否命中协商缓存。如果命中,则http返回码为304,浏览器从缓存中加载资源。


Last-Modify/If-Modify-Since


浏览器第一次请求一个资源的时候,服务器返回的header中会加上Last-Modify,Last-modify是一个时间标识该资源的最后修改时间,例如Last-Modify: Thu,31 Dec 2037 23:59:59 GMT。



当浏览器再次请求该资源时,上送的请求头中会包含If-Modify-Since,该值为缓存之前返回的Last-Modify。服务器收到If-Modify-Since后,根据资源的最后修改时间判断是否命中缓存。



如果命中缓存,则返回http304,并且不会返回资源内容,并且不会返回Last-Modify。由于对比的服务端时间,所以客户端与服务端时间差距不会导致问题。但是有时候通过最后修改时间来判断资源是否修改还是不太准确(资源变化了最后修改时间也可以一致)。于是出现了ETag/If-None-Match。


ETag/If-None-Match


与Last-Modify/If-Modify-Since不同的是,Etag/If-None-Match返回的是一个校验码(ETag: entity tag)。ETag可以保证每一个资源是唯一的,资源变化都会导致ETag变化*。服务器根据浏览器上送的If-None-Match值来判断是否命中缓存。



* ETag扩展说明


我们对ETag寄予厚望,希望它对于每一个url生成唯一的值,资源变化时ETag也发生变化。神秘的Etag是如何生成的呢?以Apache为例,ETag生成靠以下几种因子


  1. 文件的i-node编号,此i-node非彼iNode。是Linux/Unix用来识别文件的编号。是的,识别文件用的不是文件名。使用命令’ls –I’可以看到。

  2. 文件最后修改时间

  3. 文件大小

  4. 生成Etag的时候,可以使用其中一种或几种因子,使用抗碰撞散列函数来生成。所以,理论上ETag也是会重复的,只是概率小到可以忽略。


生产问题分析


背景:某次投产,某系统投产后由于强缓存设置时间不恰当导致变更的功能没有体现。后来通过变更文件路径强行解决问题。


变更上下文根,导致URL变化一定可以解决问题。但我们不可能每一次都这么做;还有,在浏览器端关闭缓存、或者清除缓存后再继续浏览、同时使用Ctrl+F5刷新,也可以解决问题,但是我们也不可能让每一个客户在投产后都做一次这个操作。


那我们怎么办呢?从问题原因来看,是将经常变化的资源缓存时间设置的过长导致的。理论上来讲,只要正确划分经常变化资源与不经常变化资源就可以解决问题。但是谁也不能保证不经常变化的资源就一定不会变化。


万一不经常变化的资源变更了怎么办呢?在资源请求的URL中增加一个参数,比如:css/main.css?v=20160105。这个参数是一个版本号,客户化在js代码中,每一次投产的时候变更一下,当这个参数变化的时候,强缓存都会失效并重新加载。这样一来,即使是不常变化的资源,投产以后也需要重新加载。这样就完美的解决了问题。


本文参考数篇技术博客、百度百科、维基百科、Apache官方说明以及自己的理解完成,如有不正确的地方欢迎斧正。



专栏作者简介 ( 点击 → 加入专栏作者 


庞健

打赏支持作者写出更多好文章,谢谢!



【今日微信公号推荐↓】

更多推荐请看值得关注的技术和设计公众号


其中推荐了包括技术设计极客 和 IT相亲相关的热门公众号。技术涵盖:Python、Web前端、Java、安卓、iOS、PHP、C/C++、.NET、Linux、数据库、运维、大数据、算法、IT职场等。点击《值得关注的技术和设计公众号》,发现精彩!


 
前端大全 更多文章 详解Javascript中的Object对象 结合个人经历总结的前端入门方法 前端不为人知的一面–前端冷知识集锦 一份优秀的前端开发工程师简历是怎么样的? 浅谈Web缓存
猜您喜欢 rails3 与 rails4 中 try 方法的不同 Gdevops北京站:干货太多,慎入!(含PPT) 马云为什么要这么下血本的推广来往 Ubuntu 16.04阿里云源 区块链的七年之痒