前言
从nodejs现世以来,Web前端开发以及运行变得越发的多样化,使得JavaScript在前端承载的业务越来越复杂,用户对于前端体验要求也越来越高,前端性能的优劣对业务营收就有了更加深远的影响。
那么如何才能在我们前端开发过程中,有效的提高优化前端性能效率,如何根据我们前端业务中更加关心的一些优化指标去完成我们的前端优化。
Navigation Timing API
Navigation Timing API是W3C性能小组引入的用于前端性能监控的API。通过该API我们可以检测到前端白屏时间、首屏时间、用户可操作的时间节点、页面总下载的时间、DNS查询的时间、TCP链接的时间等各种指标,用来提高和优化我们的前端工程。
startTime (navigationStart) |
从上一个文档卸载结束时的时间戳 若没有上一个文档,这个值将等于fetchStart |
redirectStart redirectEnd |
若页面是由redirect而来,则redirectStart和redirectEnd分别代表redirect开始和结束的时间节点。有跳转且是同域名内的重定向方才有效,否者该值为0 |
unloadEventStart unloadEventEnd |
unloadEventStartunloadEventEnd分别代表浏览器unload前一个文档的开始和结束时间节点。 若前一个文档和请求的文档是同一个域的该值有效,否则该值为0 |
fetchStart |
若要使用GET方法请求获取新资源,则在检查本地应用程序缓存之前,将当前时间记录为fetchStart。 |
domainLookupStart domainLookupEnd |
DNS查询的开始和结束时间。如果浏览器没有进行DNS查询(比如使用了DNS缓存),则两者的值都等于fetchStart |
connectStart connectEnd |
TCP建立连接和连接成功的时间节点。如果浏览器没有进行TCP连接(比如使用持久化连接webscoket),则两者都等于domainLookupEnd |
secureConnectionStart |
如果页面使用HTTPS,它的值是安全连接握手之前的时刻。如果该属性不可用,则返回undefined。如果该属性可用,但没有使用HTTPS,则返回0 |
requestStart |
客户端发起请求的时间节点,不管是从本地缓存请求还是远端请求。 |
responseStart responseEnd |
客户端收到响应回的第一个字节和最后一个字节数据,响应来源包括服务器,以及缓存 |
domLoading |
客户端开始解析html文档的时间,domLoading的值就等于document.readyState改变为loading的时间,同时触发readystatechange相关事件 |
domInteractive |
客户端完成解析html文档的时间,domLoading的值就等于document.readyState改变为interactive的时间,同时触发readystatechange相关事件 |
domContentLoadedEventStart |
html文档解析完成后,DOMContentLoaded事件触发前时间。 |
domContentLoadedEventEnd |
html文档解析完成后,DOMContentLoaded事件完成的时间。 |
domComplete |
客户端完成解析html文档同时页面资源也解析完成的时间,domLoading的值就等于document.readyState改变为complete的时间,同时触发readystatechange相关事件 |
loadEventStart loadEventEnd |
onload事件触发和结束的时间 |
通过以上API我们可以计算很多前端页面性能相关指标,如前端页面加载中普遍关注的首字节时间、白屏时间、首屏时间等。
前端页面性能分析
通过上一小节所得到Navigation API计算得到我们前端页面性能分析所需要的诸多性能指标。
let index = {}; let times = window.performance.timing;
首字节
主文档返回第一个字节的时间,是页面加载性能比较重要的指标。对用户来说一般无感知,对于开发者来说,则代表访问网络后端的整体响应耗时。
index.ttfbTime = times.responseStart - times.startTime;
白屏时间
对于前端工程最重要的指标之一,用户对于页面的第一印象来源于等待请求页面文档开始渲染的时间,也是页面资源解析完成开始进入渲染阶段的时间。整个过程包括dns查询、建立tcp链接、发送首个http请求等过程、返回html文档。
index.blankTime = (times.domInteractive || times.domLoading) - times.fetchStart;
首屏时间
另一个用户体验相关的重要指标,用户对于等待页面加载完成所用的时间近乎是苛刻的,当页面未能在用户预期的时间内渲染完成,用户离开的可能性直线上升。
index.domReadyTime = times.domContentLoadedEventEnd - times.fetchStart;
DNS查询耗时
对前端工程优化具有指导意义,前端工程中DNS查询所耗费的时间。
index.dnsTime = times.domainLookupEnd - times.domainLookupStart;
TCP链接耗时
通过这一指标我们可以了解到客户端与服务器建立链接需要的时间,进而指导我们对本地缓存、建立CDN服务器等。
index.tcpTime = times.connectEnd - times.connectStart;
页面其他资源指标
使用Resource timing API来统计资源相关的时间,performance.getEntries返回一个数组,数组的每个元素代表对应的资源的信息,该数组中PerformanceResourceTiming类型对象对应了所有请求类资源,其中initiatorType标识了该资源的类型,请求花费的时间就是duration的值。
数据上报
数据指标计算完成后,需要将计算完成的指标数据上传到服务器,完成前端页面性能监控统计。
sendBeacon
navigator对象提供的不需要在客户端上进行响应处理并且不应与其他高优先级操作竞争网络资源的单向请求的API,该API更加适合这种上报监控统计值的场景。
function reportEvent(event) { var data = JSON.stringify({ event: event, time: performance.now() }); navigator.sendBeacon('/collector', data); } document.addEventListener('visibilitychange', function() { if (document.visibilityState === 'hidden') { var sessionData = buildSessionReport(); navigator.sendBeacon('/collector', sessionData); } });
src属性降级
在不支持navigator对象的浏览器执行的降级方案,通过img或者script等get请求标签来执行get请求,使用get请求进行上报,不依赖与ajax等异步请求环境,实现优雅的降级。
var i = new Image(); i.onload = i.onerror = i.onabort = function () { i = i.onload = i.onerror = i.onabort = null; } i.src = url;
通过以上指标记录以及分析,我们能够更加清晰的完成前端页面性能的优化。
参考
navigation timing 2 Navigation Timing Level 2
Resource Timing 2 Resource Timing Level 2
Beacon Beacon