页面性能的基础因素
最近读这本小书Designing for Performance,突然想到之前一篇网络性能评价只写了一半,在这里也里也算一个做个补充。
考虑页面性能可以从两种场景,第一个场景也是因素最多的场景,即首次访问。第二个场景则是重复访问,缓存将起决定性作用。
不同的页面类型的性能优化策略也是不一样的,比如富文本页面,富多媒体页面,响应式设计的页面,游戏页面等。
浏览器也提供了很多工具和插件,比如Chrome DevTools, YSlow和PageSpeed Insight都有相应的插件,可以为开发者提供页面优化建议,这是最为简捷的方式。我在这里也主要使用Chrome进行分析,部分会配合WebPageTest工具。
要优化页面性能,了解浏览器如何渲染页面是非常重要的。以下是浏览器加载的整套流程,特别强调了对首次打开性能影响很大的网络加载流程:
其中如果HTTPS,建立连接的过程中还需要进行身份认证。
通过Chrome Inspect中的网络也可以看到整个加载数据量,以及耗时:
上图是新浪首页的数据,共发出415请求,收到6.5MB的数据,主要是图片大多,果然是个大站!
点开可以看到细节的数据耗时:
上面可以看到为请求建立与服务器的连接的开销还是比较大的。特别是在移动网络下,DNS解析,错误重连的成本都非常高,浏览器厂商也都会针对性的进行优化。
无论是以前的Pipeline, 和现在的Http2都可以达到连接复用的目的,也可以省掉这部分的开销。
Chrome的开发者工具提供了Audit, 可以提供网络相关优化和页面性能优化的建议, 比如:
在Chrome Web Store里安装YSlow和PageSpeed Insights插件,就可以进一步分析页面的性能,以及提供相关的建议。下图是YSlow的显示页面组件的内容:
在后面Statistics页签会显示资源占比,以及启用缓存(下次访问)时需要发送的请求数和资源大小:
可惜这只是理想,新浪有些资源的url带有随机数,也就是每次请求的url并不相同,会导致http cache无法使用,会重新加载。
最终新浪首页的性能显示不怎么样,YSlow给出了D,附上一堆建议,点来Grade页签就可以看到。
PageSpeed Insights会给出相类似的建议,不过个人还是建议使用PageSpeed Insights, 它给出的建议会更加符合当前业界的标准。
影响加载的主要因素
除了TTFB这种主要由网络环境和后台服务器决定的因素外,页面设计,以及包括User Agent(客户端,如浏览器)可能影响的因素包括:
连接管理 (连接数和复用)
在HTTP下本来有一个Pipeline的机制,用来达到一个HTTP连接来并发获取多个资源,但是服务器支持的少。
另一种方法是Keep alive的常连接。即User Agent尝试保持若干个连接,完成一个资源的加载后,这个连接可以继续用来加载另一个资源。这样就没有重新建立连接的开销。
而HTTP 2最大的特色就是让每一个连接支持多个资源的并发请求。请求数
减少请求数,就是要减少资源请求。比如将多张小图合成一张Sprite图片就是典型的做法。也不是请求数越少越好,极端的将所有资源都放到页面里,就是灾难了。还要从浏览器解析、渲染页面的行为上来定义。比如head中的CSS加载会阻塞页面解析,如果使用body中的<link rel="stylesheet">
,区分出可显示共域的CSS, 页尾的CSS属生,让浏览器只在需要时加载相应的CSS反而能获得更好的用户体验。参考:The future of loading CSS。请求大小
因为网络里上行和下行的速度差异,请求的大小也会对页面性能有所影响, 所以要避免不必要的请求头信息,其中Cookie最为明显。首先Cookie的存储、检索和提取就要耗费性能,再加上Cookie的应用往往是针对某个域名下或者路径下的所有请求,会导致性能的影响被放大。资源大小
如果资源较小,当然收取数据的时间就会减少,后续的处理也会更加有效率。比如JS可以使用一些工具简化,图片也可以根据不同的应用,选择不同的图片格式(算法),或者压缩比。特别是值得注意的是一些图片常常含有一些不需要的信息,比如EXIF,地理位置信息,自定义调色板等,有时会很大,也可以使用一些工具进行简化。之前遇到过一些图片网站里用户上传的图片超大的情况,就是因为图片里保留了一些Photoshop的冗余信息。参考: Image Optimization
HTML5新的响应式图片更是允许根据客户端(User Agent)的屏幕大小,而选择不同的图片。HTTP Cache
网络层的缓存模块,负责管理可缓存的资源,尽量复用缓存中已经有的可用资源。最快的请求就是不发请求,善于使用Http Cache,可以大大优化除首次打开外页面性能。但是一些网站遇到一些缓存问题时,常常简单的为url增加一个随机数,让缓存失效,这样既让客户端的整体缓存命中率下降(缓存了很多不必要的资源),强烈建议不要使用这种方法。
Http Cache的实现各个浏览器并不相同,命中率也有差异。对前端开发者也是不可见的,如果需要更加细粒度的控制缓存,可以使用H5提供的存储功能,比如Local Storage, App Cache等。参考:管好页面缓存
关键渲染路径 (Critical Rendering Path)
开始加载页面,到实际显示出内容,有一个最小的路径,即关键渲染路径。尽量缩短这条路径才能真正优化到用户的体验。
最核心的流程是浏览器收到页面数据后解析为DOM (Document Object Model), 加载CSS文件,生成CSSOM,计算各个元素的排版数据生成Layout Tree, 结合者CSSOM生成Render Tree,再交由渲染模块完成渲染显示出页面内容。
这过程中包括很多的并发行为,因为串行完成的性能不可能达到用户体验的要求。
CSS
在这个路径主要是注意一些会阻塞页面解析的元素,如CSS。前面提到CSS文件放到head和body中的方式。JavaScript
直接出现在页面中的JavaScript会阻塞页面解析, 所以需要将一些JavaScript可以改为异步加载和执行。
定义:可操作时间 (Time To Interactivity)
可操作时间是指从开始加载页面到用户可以在页面进行操作(如搜索,点击链接等)的时间。
通过优化关键渲染路径可以减少可操作时间,包括以下方法:
- 异步加载内容
- 优先加载可见内容
- 遵循CSS/JS加载的最佳实践 (看YSlow和PageSpeed Insights的报告)
- 缓存资源
- 优先保证主要用户行为尽早可用
比如下面这个Chrome DevTools显示出一个Long Frame, 从前面一个Frame这里花去了很长的时间,这一般代表卡顿的出现 (只有9fps)。
如果使用PageSpeed Insights,可以得到关于Blocking CSS的建议。