[本文属于原创,如有转载,请注明出处http://blog.csdn.net/yl02520/article/details/22300467]
现在假设用户用Chrome浏览器打开www.sohu.com主页,点击主页上的NBA导航链接,就跳转到sohu主页的NBA子页面中,在浏览完NBA子页面后,用户又想回到主页面,这次点击浏览器窗口上的后退按钮,即可回到主页面,这一切似乎很平常,只是让我们意想不到的是,这次sohu主页面的渲染几乎是瞬间完成的,比第一次打开sohu主页时的时间要缩短了几十倍。这是为什么呢?答案就是缓存,这里说缓存比较笼统,其实详细的说应该分为三块,即Chromium的磁盘缓存,WebKit的内存缓存和WebKit的页面缓存。下面分别对这三种级别的缓存进行说明。
WebKit的页面缓存(Page Cache)
用户在浏览网页时点击页面中的某个链接,浏览器就需要跳转到某个新的页面,如果新打开的链接是在原来的标签页中打开,那么原来的页面就会被销毁。那么如果用户点击后退按钮回到主页面时,就需要重新进行一序列之前已经完成的动作,例如下载HTML页面,解析HTML,解析JavaScript脚本,解析CSS脚本,构建DOM树,构建Render树,以及最后布局渲染,这些过程直接影响到网页最后显示给用户的速度。
试想一下,当我们在Google上搜索一些资源时,在搜索到的结果页面中点击某个链接后,发现不是要找的资源,需要重新回退到之前的结果页面,查看下一个搜索结果,如果在返回时需要做上述一系列耗时的操作,网页返回的时间就会很长,直接造成不好的用户体验。
基于以上考虑,WebKit对上述过程进行了优化,主页面切换到一个新的页面时,主页面不会被销毁,而会被保存起来,暂定网页的消息循环,直到网页再次被切换回来。这个过程可以类似理解成主页面暂时被隐藏了,就好象在浏览器中打开了一个看不见的标签页,当用户通过后退或前进按钮回到主页面时,主页面就会被显示出来,而又重新打开页面的消息循环。
WebKit的内存缓存(Memory Cache)
WebKit在解析HTML文档时,如果发现了外部资源链接,就会生成一个资源请求,但是不会立即通过网络去下载资源,而是现在自身的内存缓存中查询,检查是否存在与请求资源URL相匹配的资源文件,如果存在就立即返回缓存中的资源,如果缓存中查询失败时,就在缓存中添加一个URL与资源文件的索引,以防下次使用。
WebKit的代码中存在一个MemoryCache类,每个WebKit实例都只存在一个MemoryCache对象,在MemoryCache对象中存放中Resource的指针链表,Resource的链表是采用LRU(最近最少使用)算法来管理的。现在假设某个资源文件被浏览器请求,在MemoryCache中查询到该资源后,需要先从Resource的链表中删除该Resource对象,然后从链表头部重新添加该Resource对象。
Chromoium的磁盘缓存(Disk Cacke)
这里为什么写Chromium的磁盘缓存,而不是WebKit的磁盘缓存呢?因为在Chromium浏览器中WebKit是运行在沙盒进程(渲染进程)中,该进程没有访问本地文件系统的能力,关于Chromium的架构,请参看笔者发布的另一篇博文“Chromium浏览器的多进程架构”。所以写磁盘这样的操作都是发生在浏览器进程中。
关于Chromium加载资源的流程请参考另一篇博文“Chromium的多进程资源加载”,当浏览器进程接收到渲染进程发送过来的资源请求时,浏览器进程会先检查本地的缓存,如果没有找到与资源URL匹配的文件或者缓存文件失效时,才发送网络请求,去服务器上下载资源,当资源下载完毕后,浏览器进程会保存该资源文件到磁盘上,以防下次浏览器重新启动时使用。