浏览器缓存技术是提升前端性能一个非常重要的手段。在当前浏览器各方面性能都大幅提升的情况下,前端性能的瓶颈更多的集中在资源请求方面。
原理
说浏览器缓存,其实就是在说:
浏览器实现缓存的机制
HTTP通讯协议定义的关于缓存的字段内容
先让我们回顾下,当我们在浏览器的地址栏输入URL之后发生哪些过程:
DNS服务器
解析域名,找到对应服务器的IP地址
;和服务器建立
TCP连接
;基于
TCP连接
进行HTTP的Req/Res
,服务器会根据HTTP请求到数据服务器
取出相应的资源,并返回给前端;浏览器获取HTML文档,并开始
解析
;遇到
link
标签,加载href
指向的地址,并解析css
,遇到带有src
属性,且没有声明async或defer
属性的script
标签时,同步加载远程js脚本
,同步解析js脚本,同步执行js脚本,浏览器的GUI渲染
过程处于挂起状态。当js执行完毕后,继续后面的HTML解析的工作;DOM树
构建完毕,CSS Rules
解析完毕;由
DOM树
和CSS Rules
一起构建Render树
。和DOM树
不一样的地方在于Render树
是带有DOM样式的,伪元素
不会在DOM树
中出现,而会在Render树
中出现。Render树
构建完毕后开始repaint
和reflow
过程,即渲染
。最后将页面呈现给用户。
在这些过程当中:
过程1:远程服务器可能是
代理服务器
,代理服务器可进行缓存匹配,可将缓存的资源直接返回给前端,降低对于源服务器的压力,同时还能做均衡负载;过程3:
数据服务器
可以进行memcached
,进行数据库层面的缓存,可将上一次的查询结果缓存到内存当中,下次再查询相同的数据时,直接从内容从取出数据;过程5:可以利用
浏览器缓存
。这也是本文所讲的主要内容。
当我访问 SegmentFault 的个人主页时:
在浏览器首次加载并解析HTML文档时,会维护一个资源的缓存池
,缓存池
里存放了加载的包括css,js,图片,字体等文件,URL
是这些资源的唯一标识。浏览器会根据HTTP响应字段
来决定这些资源是否能缓存以及缓存的时间等:
Expires
字段设置了资源过期的时间。Cache-control
字段的内容较多,例如no-cache会决定资源不会缓存;max-age指客户机可以接收生存期不大于指定时间(以秒为单位)的响应。具体的内容(图)Etag
:资源的唯一标识符Last-Modified
: 资源最后一次修改的时间
如果将页面刷新,此时加载同一份HTML文件,同样遇到外联的css,js,图片和字体等文件。此时浏览器首先会从缓存池中寻找,寻找的标记为资源的URL地址。
如果缓存池中有对应的资源,通过查询Expires字段
和Cache-control字段
的值,如果没有过期,那么会从缓存池中获取资源,不会进行HTTP请求。
若资源过期,则会进行HTTP请求,此时浏览器会检查缓存池中资源是否具有Etag
和Last-modified
.如果有Etag
字段(资源的唯一标识符,只要发生修改,其值都会发生改变)的内容,那么HTTP请求中会带上If-none-match
字段,其值为Etag
字段的内容,如果有Last-Modified
字段(最后一次修改的时间),那么HTTP请求会带上If-Modified-Since
字段,其值为Last-modified
字段的内容。服务器根据HTTP请求去匹配If-none-match
和If-Modified-Since
的内容。
当If-none-match
的内容匹配不成功时,服务器会返回状态码为200 success
的响应及所有的资源内容,并设置新的和缓存相关的字段内容,若匹配成功返回304 Not Modify
响应状态,但是不会返回资源内容。此时浏览器仍然是从资源池中获取资源。
If-Modified-Since
的处理方式和If-none-match
相同。
当以上2个字段在HTTP请求中都存在时,会优先匹配If-None-Match
的内容,只有相同的情况下才会去匹配If-Modified-Since
的内容,来绝对是返回200还是304。
总结
页面首次加载
页面刷新
实践
之前在实习的过程中,对于浏览器缓存所做的工作之一就是每次在发版前通过gulp给资源打上版本号,例如:
"/css/style.css" => "/dist/css/style.css?v=1d87bebe";
"/js/script1.js" => "/dist/script1.js?v=61e0be79";
这样再去首页去加载这些资源的时候,因为在资源池里找不到对应的URL,因此会发送HTTP请求,从服务器端获取最新的资源。 具体实践过程请自行google
。
参考资料
《webkit技术内幕》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。