网页性能优化是一个工程学问题,理论上正确到实践中未必合适。网页性能优化要根据项目实际情况来应变,没有银弹。
HTTP、网络层面优化
上边 4 图分别是请求 www.zhihu.com 80 端口和 443 端口的 Timing 图情况,可以看出从键入网址到页面加载完成,主要经过以下几个阶段:
- 查询 DNS(DNS lookup)
- 初始化 TCP 链接(Initial connection)、https 还包括 SSL
- 发送 http 请求(Request sent)
- 等待服务器响应(Waiting)
- 响应内容下载(Content Downloaded)
所以优化网页性能也应该从减少以上几个阶段的时间入手,下边主要讨论前端所能涉及到的,非前端所能改变的不会有太多提及。
首先是最直接减少以上 5 个阶段时间的方案(方案 A):
- 减少 DNS 查询时间:同一网页中不要使用过多域名下的资源
- 减少 初始化 TCP 链接的时间,减少页面中资源的数量,比如合并 CSS、js,使用雪碧图等等
- 减少发送 http 请求所需时间,这一方面主要是从减少请求内容入手,比如不滥用 cookies,该使用 localstorage 的时候就是用 localstorage
- 减少等待服务器响应时间,这一方面主要是服务器响应速度问题,主要从服务器端来解决,服务器性能、服务器端代码性能、服务器网络等等,使用 CDN 是减少这一阶段时间的有效手段,一般来说北京用户访问北京的服务器总比访问美国的服务器要快得多。
- 减少响应内容下载的时间,开启 gzip 压缩是一个有效的手段,4 中提到的 CDN 也是如此。
在上边的方案中我没有考虑浏览器的并行下载能力,实际上浏览器是可以同时并行下载多个资源的,但是一般浏览器都会对同一个域名下并行下载的资源数量作出限制,一般每个域名允许并行下载的数量是 4-10 个(取决于浏览器)。那么考虑到利用浏览器的并行能力,还有以下几个点(方案 B):
- 拆分大的 CSS、js 等文件,这一点和上边的 2 中提到的方案是矛盾的,在一开始我就提到,网页性能优化是工程学问题,两权相害取其轻。
- 为了解决浏览器对并行下载数量的限制,我们会在同一个页面中使用多个域名下的资源,这一点又和上边 1 中的方案是矛盾的。实际中往往是将图片、js、css 等静态内容放到单独的 CDN,这样静态文件 CDN 还没有 cookies,能够减少发送 http 请求的时间,也能优雅的在不同项目中实现资源复用,用户在打开你的其他网站时也不需要再次加载。
还有一点是没有提到的,那就是缓存,直接从本地加载才是最快的。实际中往往会对 CSS、js、图片等内容设置很长的缓存时间,当文件内容变更时直接修改文件名,前端的工程化使得 xx-hash.js 这种方式变得很简单。对于一些公共的库(比如 jQuery)使用公共的 CDN (如 bootcdn)也是不错的方案,这样会使得即使用户是第一次打开你的网站也有很可能不需要再次请求。
写到这里看起来有些乱,有些地方有矛盾,我也没有总结类似于雅虎军规类似的东西,还是那一句话,没有银弹。把握住根源,从网络层面的各个阶段来着手,根据项目具体情况具体分析,性能优化是需要实测的,看起来很合理的方案,实际中反而可能出现负面效果。充分利用开发者工具,如 Audits面板、Network 面板下的 Timing,他们作为参考让你能够容易发现拖后腿的环节,然后可以采取针对性的方案。
同时,技术是一直在进步的,优化方案也不会一成不变,以前感觉不错的方案,将来可能会变得无效;有副作用的方案,将来可能会变成最优解。比如 HTTP/2.0 的多路复用,多路复用允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息,那么方案 A 中的 2,合并文件来减少链接从而减少建立 TCP 连接的时间效果就不再明显,同时使用多个域名的 CDN 来破解浏览器并行数量的限制也就并非必要了。这也提醒我们,技术的进步可能比我们费心的优化有效百倍。
代码层面
代码层面的优化方案主要有懒加载、动态加载、预加载等方案。
- 懒加载是指在需要资源的时候才进行加载,触发某些条件才开始异步加载,常见的是图片懒加载,一般是图片进入或者快进入视区的时候才开始加载图片。懒加载能够减少请求数或延迟请求数,有效的减少服务器压力,同时也提升了用户的首屏加载速度。
- 动态加载类似于懒加载,和懒加载的不同之处在于加载完首屏资源后就会自动加载其他资源,而不等待触发某些条件。看起来和懒加载极其相似,甚至也可以归类于触发条件为首屏资源加载完毕的懒加载。
- 预加载和懒加载相反,会提前下载好资源,等需要的时候直接从缓存读取内容。比如需要翻页的内容,提前读取下一页甚至下 2 页的内容。预加载有时会增大服务器压力,换取更好的用户体验,可以使用户的操作得到最快的响应。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。