关于前端页面的性能优化是个老生常谈的话题,各种技术博客已经也已经罗列出了各种方案,但是要优化,就得找出问题,像盲人摸象一样瞎优化其实并不会有特别明显的效果。
chrome-dev-tools应该是做为FE最熟悉的工具,开发时必备的调试神器。其他先不说,今天我们只看network.
主要功能大家应该都会用,我只说几个比较重要的点(指标)。
1. Network 面板突出显示两种事件:DOMContentLoaded
和 load
图片底部信息栏中的蓝色部分为DOMContentLoaded事件触发的时间,时间轴中的蓝色线也代表它在整个时间轴中的位置,看起来更直观。
那么DOMContentLoaded代表什么呢?其实就是页面dom文档加载完成,翻译成代表的话就是
document.ready = function () { ... } //or $(document).ready({...}); //or $(function(){..});
所以,这个时间应该越小越好,那么怎么来优化使这个时间更小呢?
首先,我们应该知道在这个时间段内发生了什么,从上图可以看到首先第一个加载的是document,即为我们发送请求后,server端经过一系列的处理返回的html文档,可以看出这个耗时还是比较长的,鼠标悬停到 Timeline 图表内的资源上时,可以看到更详细的信息。
这几个字段都是什么意思呢?看一下官方的文档
Queuing如果某个请求正在排队,则指示:
- 请求已被渲染引擎推迟,因为该请求的优先级被视为低于关键资源(例如脚本/样式)的优先级。 图像经常发生这种情况。
- 请求已被暂停,以等待将要释放的不可用 TCP 套接字。
- 请求已被暂停,因为在 HTTP 1 上,浏览器仅允许每个源拥有六个 TCP 连接。
- 生成磁盘缓存条目所用的时间(通常非常迅速)
Stalled/Blocking请求等待发送所用的时间。 可以是等待 Queueing 中介绍的任何一个原因。 此外,此时间包含代理协商所用的任何时间。
Proxy Negotiation与代理服务器连接协商所用的时间。
DNS Lookup执行 DNS 查询所用的时间。 页面上的每一个新域都需要完整的往返才能执行 DNS 查询。
Initial Connection / Connecting建立连接所用的时间,包括 TCP 握手/重试和协商 SSL 的时间。
SSL完成 SSL 握手所用的时间。
Request Sent / Sending发出网络请求所用的时间。 通常不到一毫秒。
Waiting (TTFB)等待初始响应所用的时间,也称为至第一字节的时间。 此时间将捕捉到服务器往返的延迟时间,以及等待服务器传送响应所用的时间。
Content Download / Downloading接收响应数据所用的时间。
可以看出上图中中TTFB的时间比较长,而官方的建议是将此值控制在 200 毫秒以下。而导致这个耗时较长的原因有两个:
- 客户端与服务器之间的网络条件较差
- 服务器应用的响应慢
解决办法是首先尽可能缩减网络。理想的情况是将应用托管在本地,然后查看 TTFB 是否仍然很长。如果仍然很长,则需要优化应用的响应速度。有很多潜在因素都可能会延缓服务器响应,例如应用逻辑缓慢、数据库查询缓慢、路由缓慢、框架、库、资源CPU不足或内存不足等。所以需要server端的学生优化一下了,可以是优化数据库查询、为特定部分的内容实现缓存,或者修改网络服务器配置。
所以当你看到timeline上有大片绿色时,别犹豫,把优化的艰巨任务让给server吧,他们的工作应该又饱合了一些。
参考:
https://developers.google.com/web/tools/chrome-devtools/network-performance/understanding-resource-timing?hl=zh-cn
当TTFB优化好以后,那该fe上场了。接下来我们能做什么呢?
1. 合并资源,减少请求
当DOM加载完成后便开始解析,其他的资源也开始加入请求队列,这时就会有个问题,如果是http1协议的话并发的http请求数是有限制的,chorme是6个。其他浏览器稍有差异,不过大概也是在这个数量。以下为浏览器具体的限制:
IE 6 and 7: 2
IE 8: 6
IE 9: 6
IE 10: 8
IE 11: 8
Firefox 2: 2
Firefox 3: 6
Firefox 4 to 46: 6
Opera 9.63: 4
Opera 10: 8
Opera 11 and 12: 6
Chrome 1 and 2: 6
Chrome 3: 4
Chrome 4 to 23: 6
Safari 3 and 4: 4
所以为了尽可能减少限制,更多的加载资源就要把该合并的合并,该压缩的压缩。
2. 静态资源上传CDN
上边说到的http限制是有一个条件就是同一主机下才有的,所以解决限制的办法还有一个就是域分片。也就是在应用上设置多个子域,以便提供资源。然后,在子域之间平均分配正在提供的资源。而CDN本来就是用来最近的访问资源,所以CDN是一定要用的。
3. 加载顺序
加载顺序这个大家知道,因为js会阻塞DOM的解析,所以js文件尽量放到文档底部,而css必须要放在文档头部,因为会根据css文件生成cssDOM,然后由DOM和cssDOM合并才能产生渲染树,有了渲染树才会去接下来的布局和解析。
4. aync和defer
defter
当 HTML 文档被解析时如果遇见 defer 脚本,则在后台加载脚本,文档解析过程不中断,而等文档解析结束之后,defer 脚本执行。另外,defer 脚本的执行顺序与定义时的位置有关。过程如下图:
async
当 HTML 文档被解析时如果遇见 async 脚本,则在后台加载脚本,文档解析过程不中断。脚本加载完成后,文档停止解析,脚本执行,执行结束后文档继续解析。过程如下图:
如果 script 标签中包含 defer,那么这一块脚本将不会影响 HTML 文档的解析,而是等到 HTML 解析完成后才会执行。而 DOMContentLoaded 只有在 defer 脚本执行结束后才会被触发。 所以这意味着什么呢?HTML 文档解析不受影响,等 DOM 构建完成之后 defer 脚本执行,但脚本执行之前需要等待 CSSOM 构建完成。在 DOM、CSSOM 构建完毕,defer 脚本执行完成之后,DOMContentLoaded 事件触发。
如果 script 标签中包含 async,则 HTML 文档构建不受影响,解析完毕后,DOMContentLoaded 触发,而不需要等待 async 脚本执行、样式表加载等等。
需要注意的是用async时,在文件加载完成后就会立即执行不会等文档加载完,所以很有可能不是按照原本的顺序来执行的。如果js前后有依赖性,用async,就很有可能出错。