本文将探讨浏览器渲染的loading过程,主要有2个目的:
- 了解浏览器在loading过程中的实现细节,具体都做了什么
- 研究如何根据浏览器的实现原理进行优化,提升页面响应速度
由于loading和parsing是相互交织、错综复杂的,这里面有大量的知识点,为了避免过于发散本文将不会对每个细节都深入研究,而是将重点放在开发中容易控制的部分(Web前端和Web Server),同时由于浏览器种类繁多且不同版本间差距很大,本文将侧重一些较新的浏览器特性
现有知识
提升页面性能方面已经有很多前人的优秀经验了,如Best Practices for Speeding Up Your Web Site和Web Performance Best Practices
本文主要专注其中加载部分的优化,总结起来主要有以下几点:
- 带宽
- 使用CDN
- 压缩js、css,图片优化
- HTTP优化
- 减少转向
- 减少请求数
- 缓存
- 尽早Flush
- 使用gzip
- 减少cookie
- 使用GET
- DNS优化
- 减少域名解析时间
- 增多域名提高并发
- JavaScript
- 放页面底部
- defer/async
- CSS
- 放页面头部
- 避免@import
- 其它
- 预加载
接下来就从浏览器各个部分的实现来梳理性能优化方法
network
首先是网络层部分,这方面的实现大部分是通过调用操作系统或gui框架提供的api
DNS
为了应对DNS查询的延迟问题,一些新的浏览器会缓存或预解析DNS,如当Chrome访问google页面的搜索结果时,它会取出链接中的域名进行预解析
当然,Chrome并不是每次都将页面中的所有链接的域名都拿来预解析,为了既提升用户体验又不会对DNS造成太大负担,Chrome做了很多细节的优化,如通过学习用户之前的行为来进行判断
Chrome在启动时还会预先解析用户常去的网站,具体可以参考DNS Prefetching,当前Chrome中的DNS缓存情况可以通过net-internals页面来察看
为了帮助浏览器更好地进行DNS的预解析,可以在html中加上以下这句标签来提示浏览器
<link rel="dns-prefetch" href="//HOSTNAME.com">
除此之外还可以使用HTTP header中的X-DNS-Prefetch-Control来控制浏览器是否进行预解析,它有on和off两个值,更详细的信息请参考Controlling DNS prefetching
CDN
本文不打算详细讨论这个话题,感兴趣的读者可以阅读Content delivery network
在性能方面与此相关的一个问题是用户可能使用自定义的DNS,如OpenDNS或Google的8.8.8.8,需要注意对这种情况进行处理
link prefetch
由于Web页面加载是同步模型,这意味着浏览器在执行js操作时需要将后续html的加载和解析暂停,因为js中有可能会调用document.write来改变dom节点,很多浏览器除了html之外还会将css的加载暂停,因为js可能会获取dom节点的样式信息,这个暂停会导致页面展现速度变慢,为了应对这个问题,Mozilla等浏览器会在执行js的同时简单解析后面的html,提取出链接地址提前下载,注意这里仅是先下载内容,并不会开始解析和执行
这一行为还可以通过在页面中加入以下标签来提示浏览器
<link rel="prefetch" href="http://">
但这种写法目前并没有成为正式的标准,也只有Mozilla真正实现了该功能,可以看看Link prefetching FAQ
WebKit也在尝试该功能,具体实现是在HTMLLinkElement的process成员函数中,它会调用ResourceHandle::prepareForURL()函数,目前从实现看它是仅仅用做DNS预解析的,和Mozilla对这个属性的处理不一致