为什么要监控页面性能?
一个页面性能差的话会大大影响用户体验. 用户打开页面等待的太久, 可能会直接关掉页面, 甚至就不再使用了, 这种情况在移动端更加明显, 移动端用户对页面响应延迟容忍度很低.
虽然页面性能很重要, 但是在实际使用中, 页面性能差的情况并不少见. 首先, 在产品的迭代演进过程中, 页面性能可能会被忽略, 性能随着版本迭代而有所衰减; 其次, 性能优化是一项复杂而挑战的事情, 需要明确的优化方向和具体的优化手段才能快速落地取效.
所以我们需要一个性能监控系统, 持续监控和预警页面性能的状况, 并且在发现瓶颈的时候指导优化工作.
理解 Navigation Timing API 的性能指标
为了帮助开发者更好地衡量和改进前端页面性能, W3C 性能小组引入了 Navigation Timing API , 实现了自动, 精准的页面性能打点; 开发者可以通过 Windows.performance 属性获取.
下图是 W3C 第一版的 Navigation Timing 的处理模型. 从当前浏览器窗口卸载旧页面开始, 到新页面加载完成, 整个过程一共被切分为 9 个小块: 提示卸载旧文档, 重定向 / 卸载, 应用缓存, DNS 解析, TCP 握手, HTTP 请求处理, HTTP 响应处理, DOM 处理, 文档装载完成. 每个小块的首尾, 中间做事件分界, 取 Unix 时间戳, 两两事件之间计算时间差, 从而获取中间过程的耗时 (精确到毫秒级别).
image.PNG
上图是 Level 1 的规范, 2012 年底进入候选建议阶段, 至今仍在日常使用中; 但是在 W3C 的议程上, 它已经功成身退, 让位给了精度更高, 功能更强大, 层次更分明的 Level 2(处理模型如下图). 比如独立划分出来的 Resource Timing, 使得我们可以获取具体资源的详细耗时信息
image.PNG
指标解读
image.PNG
采集页面性能的关键指标
image.PNG
确定统计起始点 (navigationStart vs fetchStart )
页面性能统计的起始点时间, 应该是用户输入网址回车后开始等待的时间. 一个是通过 navigationStart 获取, 相当于在 URL 输入栏回车或者页面按 F5 刷新的时间点; 另外一个是通过 fetchStart, 相当于浏览器准备好使用 HTTP 请求获取文档的时间.
从开发者实际分析使用的场景, 浏览器重定向, 卸载页面的耗时对页面加载分析并无太大作用; 通常建议使用 fetchStart 作为统计起始点.
首字节
主文档返回第一个字节的时间, 是页面加载性能比较重要的指标. 对用户来说一般无感知, 对于开发者来说, 则代表访问网络后端的整体响应耗时.
白屏时间
用户看到页面展示出现一个元素的时间. 很多人认为白屏时间是页面返回的首字节时间, 但这样其实并不精确, 因为头部资源还没加载完毕, 页面也是白屏.
相对来说具备「白屏时间」统计意义的指标, 可以取 domLoading - fetchStart, 此时页面开始解析 DOM 树, 页面渲染的第一个元素也会很快出现.
从 W3C Navigation Timing Level 2 的方案设计, 可以直接采用 domInteractive - fetchStart , 此时页面资源加载完成, 即将进入渲染环节.
首屏时间
首屏时间是指页面第一屏所有资源完整展示的时间. 这是一个对用户来说非常直接的体验指标, 但是对于前端却是一个非常难以统计衡量的指标.
具备一定意义上的指标可以使用, domContentLoadedEventEnd - fetchStart, 甚至使用 loadEventStart - fetchStart, 此时页面 DOM 树已经解析完成并且显示内容.
以下给出统计页面性能指标的方法.
- let times = {};
- let t = Windows.performance.timing;
- // 优先使用 navigation v2 https://www.w3.org/TR/navigation-timing-2/
- if (typeof win.PerformanceNavigationTiming === 'function') {
- try {
- var nt2Timing = performance.getEntriesByType('navigation')[0]
- if (nt2Timing) {
- t = nt2Timing
- }
- } catch (err) {
- }
- }
- // 重定向时间
- times.redirectTime = t.redirectEnd - t.redirectStart;
- //dns 查询耗时
- times.dnsTime = t.domainLookupEnd - t.domainLookupStart;
- //TTFB 读取页面第一个字节的时间
- times.ttfbTime = t.responseStart - t.navigationStart;
- //DNS 缓存时间
- times.appcacheTime = t.domainLookupStart - t.fetchStart;
- // 卸载页面的时间
- times.unloadTime = t.unloadEventEnd - t.unloadEventStart;
- //tcp 连接耗时
- times.tcpTime = t.connectEnd - t.connectStart;
- //request 请求耗时
- times.reqTime = t.responseEnd - t.responseStart;
- // 解析 dom 树耗时
- times.analysisTime = t.domComplete - t.domInteractive;
- // 白屏时间
- times.blankTime = (t.domInteractive || t.domLoading) - t.fetchStart;
- //domReadyTime
- times.domReadyTime = t.domContentLoadedEventEnd - t.fetchStart;
注意点
通过 Windows.performance.timing 所获的的页面渲染所相关的数据, 在 SPA 应用中改变了 url 但不刷新页面的情况下是不会更新的. 因此仅仅通过该 API 是无法获得每一个子路由所对应的页面渲染的时间. 如果需要上报切换路由情况下每一个子页面重新 render 的时间, 需要自定义上报.
来源: http://www.jianshu.com/p/5acf5b320351