web前端性能监控

web 的性能一定程度上影响了用户留存率,Google DoubleClick研究表明:如果一个移动端页面加载时长超过 3 秒,用户就会放弃而离开。BBC 发现网页加载时长每增加 1 秒,用户就会流失 10%

一. 监控指标

Chrome团队提出了一种 RAIL 模型来衡量应用性能,即: Response(响应)、Animation(动画)、Idle(浏览器空置状态)和 Load(加载)。如果在每个模块,都可以达到性能优化的目标值,那么最终用户感受到的将会是极致的体验。

  1. Response(响应) - 100ms
    如果用户点击了一个按钮,你需要保证在用户察觉出延迟之前就得到反馈。我们需要:
    (1) 在首次收到输入时,在100毫秒内得到回应。
    (2) 如果最终结果还需要花更长的时间得到,那也要给用户一个“加载中”的标识,或是颜色的变更,告诉用户“本产品已经接收到了指令,还在处理中”,不至于让用户自我怀疑。
  2. Animation(动画) - 16ms
    动画包含了以下概念:视觉动画、滚动、拖拽等。
    合理地动画,每一帧动画要在16毫秒内完成,才能达到60FPS1000ms/60 ~= 16.6 ms
  3. Idle(浏览器空置状态) - 50ms
    浏览器空闲的时候再处理耗时任务。合理地应用浏览器空闲时间,最好把时间以 50 毫秒为单位分组。因为应用应该在 100 毫秒内给出响应,不应该出现一个模板渲染2 秒之久。
    备注:Optimistic UI
  4. Load(加载) - 1s
    我们需要把最需要传达的内容在1 秒内渲染出来。我们要优先考虑关键渲染路径,将所有不需要在加载时处理的任务延迟到浏览器空闲时再处理。

二. Performance

浏览器提供的performance api是性能监控数据的主要来源。performance 提供高精度的时间戳,精度可达纳秒级别,且不会随操作系统时间设置的影响。目前主流浏览器都支持。

  1. performance.timing对象
    performance对象的timing属性指向一个对象,它包含了各种与浏览器性能有关的时间数据,提供浏览器处理网页各个阶段的耗时。
    image.png
connectEnd: 1588117852573
connectStart: 1588117852492
domComplete: 1588117854411
domContentLoadedEventEnd: 1588117853956
domContentLoadedEventStart: 1588117853955
domInteractive: 1588117853955
domLoading: 1588117852694
domainLookupEnd: 1588117852492
domainLookupStart: 1588117852150
fetchStart: 1588117852137
loadEventEnd: 1588117854441
loadEventStart: 1588117854411
navigationStart: 1588117852120
redirectEnd: 0
redirectStart: 0
requestStart: 1588117852573
responseEnd: 1588117852670
responseStart: 1588117852667
secureConnectionStart: 1588117852505
unloadEventEnd: 1588117852680
unloadEventStart: 1588117852678

navigationStart:当前浏览器窗口的前一个网页关闭,发生unload事件时的Unix毫秒时间戳。如果没有前一个网页,则等于fetchStart属性。
unloadEventStart:如果前一个网页与当前网页属于同一个域名,则返回前一个网页的unload事件发生时的Unix毫秒时间戳。如果没有前一个网页,或者之前的网页跳转不是在同一个域名内,则返回值为0
unloadEventEnd:如果前一个网页与当前网页属于同一个域名,则返回前一个网页unload事件的回调函数结束时的Unix毫秒时间戳。如果没有前一个网页,或者之前的网页跳转不是在同一个域名内,则返回值为0
redirectStart:返回第一个HTTP跳转开始时的Unix毫秒时间戳。如果没有跳转,或者不是同一个域名内部的跳转,则返回值为0
redirectEnd:返回最后一个HTTP跳转结束时(即跳转回应的最后一个字节接受完成时)的Unix毫秒时间戳。如果没有跳转,或者不是同一个域名内部的跳转,则返回值为0
fetchStart:返回浏览器准备使用HTTP请求读取文档时的Unix毫秒时间戳。该事件在网页查询本地缓存之前发生。
domainLookupStart:返回域名查询开始时的Unix毫秒时间戳。如果使用持久连接,或者信息是从本地缓存获取的,则返回值等同于fetchStart属性的值。
domainLookupEnd:返回域名查询结束时的Unix毫秒时间戳。如果使用持久连接,或者信息是从本地缓存获取的,则返回值等同于fetchStart属性的值。
connectStart:返回HTTP请求开始向服务器发送时的Unix毫秒时间戳。如果使用持久连接(persistent connection),则返回值等同于fetchStart属性的值。
connectEnd:返回浏览器与服务器之间的连接建立时的Unix毫秒时间戳。如果建立的是持久连接,则返回值等同于fetchStart属性的值。连接建立指的是所有握手和认证过程全部结束。
secureConnectionStart:返回浏览器与服务器开始安全链接的握手时的Unix毫秒时间戳。如果当前网页不要求安全连接,则返回0。
requestStart:返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的Unix毫秒时间戳。
responseStart:返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix毫秒时间戳。
responseEnd:返回浏览器从服务器收到(或从本地缓存读取)最后一个字节时(如果在此之前HTTP连接已经关闭,则返回关闭时)的Unix毫秒时间戳。
domLoading:返回当前网页DOM结构开始解析时(即Document.readyState属性变为 “loading” 、相应的readystatechange事件触发时)的Unix毫秒时间戳。
domInteractive:返回当前网页DOM结构结束解析、开始加载内嵌资源时(即Document.readyState属性变为“interactive”、相应的readystatechange事件触发时)的Unix毫秒时间戳。
domContentLoadedEventStart:返回当前网页DOMContentLoaded事件发生时(即DOM结构解析完毕、所有脚本开始运行时)的Unix毫秒时间戳。
domContentLoadedEventEnd:返回当前网页所有需要执行的脚本执行完成时的Unix毫秒时间戳。
domComplete:返回当前网页DOM结构生成时(即Document.readyState属性变为“complete”,以及相应的readystatechange事件发生时)的Unix毫秒时间戳。
loadEventStart:返回当前网页load事件的回调函数开始时的Unix毫秒时间戳。如果该事件还没有发生,返回0
loadEventEnd:返回当前网页load事件的回调函数运行结束时的Unix毫秒时间戳。如果该事件还没有发生,返回0
根据上面这些属性,可以计算出网页加载各个阶段的耗时。比如,网页加载整个过程的耗时的计算方法如下:

var t = performance.timing; 
var pageLoadTime = t.loadEventEnd - t.navigationStart;

重定向次数:performance.navigation.redirectCount
重定向耗时: redirectEnd - redirectStart
DNS 解析耗时: domainLookupEnd - domainLookupStart
TCP 连接耗时: connectEnd - connectStart
SSL 安全连接耗时: connectEnd - secureConnectionStart
网络请求耗时 (TTFB): responseStart - requestStart
数据传输耗时: responseEnd - responseStart
DOM 解析耗时: domInteractive - responseEnd
资源加载耗时:loadEventStart - domContentLoadedEventEnd
首包时间: responseStart - domainLookupStart
白屏时间: responseEnd - fetchStart
首次可交互时间: domInteractive - fetchStart
DOM Ready 时间: domContentLoadEventEnd - fetchStart
页面完全加载时间: loadEventStart - fetchStart
http 头部大小: transferSize - encodedBodySize

  1. performance.now()
    返回当前网页自从performance.timing.navigationStart到当前时间之间的毫秒数。
performance.now()
//61919.05499999848
Date.now() - (performance.timing.navigationStart + performance.now())
//-0.965087890625

performance.timing.navigationStart加上performance.now(),近似等于Date.now()。但是,由于performance.now()带有小数,因此精度更高。
通过两次调用performance.now()方法,可以得到间隔的准确时间,用来衡量某种操作的耗时。

var start = performance.now();
for(let i = 0; i < 1000; i++) {}
var end = performance.now();

console.log('耗时:' + (end - start) + '毫秒。');
//耗时:0.029999995604157448毫秒。
  1. performance.mark()
    mark方法用于为相应的视点做标记。
window.performance.mark('mark1');
window.performance.mark('mark2');
window.performance.getEntriesByType('mark');

clearMarks方法用于清除标记,如果不加参数,就表示清除所有标记。

window.peformance.clearMarks('mark1');
window.performance.clearMarks();
  1. performance.getEntries()
    浏览器获取网页时,会对网页中每一个对象(脚本文件、样式表、图片文件等等)发出一个HTTP请求。performance.getEntries方法以数组形式,返回这些请求的时间统计信息,有多少个请求,返回数组就会有多少个成员。
window.performance.getEntries()[0].duration
// 1567.8199999965727
  1. performance.navigation对象
    除了时间信息,performance还可以提供一些用户行为信息,主要都存放在performance.navigation对象上面。
PerformanceNavigation {type: 1, redirectCount: 0}

(1) performance.navigation.type
该属性返回一个整数值,表示网页的加载来源,可能有以下4种情况:
0:网页通过点击链接、地址栏输入、表单提交、脚本操作等方式加载,相当于常数performance.navigation.TYPE_NAVIGATENEXT
1:网页通过“重新加载”按钮或者location.reload()方法加载,相当于常数performance.navigation.TYPE_RELOAD
2:网页通过“前进”或“后退”按钮加载,相当于常数performance.navigation.TYPE_BACK_FORWARD
255:任何其他来源的加载,相当于常数performance.navigation.TYPE_UNDEFINED
(2) performance.navigation.redirectCount
该属性表示当前网页经过了多少次重定向跳转。

  1. performance.memory
    描述内存多少,是在Chrome中添加的一个非标准属性。
jsHeapSizeLimit: 2172649472
totalJSHeapSize: 47238861
usedJSHeapSize: 42139525

jsHeapSizeLimit: 内存大小限制
totalJSHeapSize: 可使用的内存
usedJSHeapSize: JS对象(包括V8引擎内部对象)占用的内存,不能大于totalJSHeapSize,如果大于,有可能出现了内存泄漏

  1. Chrome Devtools Performance
    https://segmentfault.com/a/1190000011516068
    image.png

三. React Profile

  1. 开启高亮更新


    image.png
  2. 查看性能数据
    image.png

    从概念上讲,React 分两个阶段工作:
    (1) render(渲染)阶段,确定需要对DOM进行哪些更改。在此阶段,React调用 render 方法,然后将结果与之前的渲染进行比较。
    (2) commit(提交)阶段,是 React 做出任何更新的阶段。(对于 React DOM 来时,这是React插入,更新和删除DOM节点的时候。) React 也在这个阶段调用 componentDidMountcomponentDidUpdate 等生命周期函数。
    DevTools Profiler(分析器) 根据 commits(提交) 对性能信息进行分组。commits(提交) 显示在靠近 Profiler(分析器) 顶部的条形图中。
    每个条形图的颜色和高度对应于 commit(提交) 渲染所需的时间 (较高的黄色竖条比较短的蓝色竖条耗时更长)。
  3. 过滤commits
    image.png
  4. 火焰图表(Flame chart
    火焰图表视图表示特定 commits(提交) 对应的应用的状态。 图表中的每个横条代表一个 React 组件(例如 AppNav )。 横条的大小和颜色表示渲染组件及其子组件所需的时间。 (横条的宽度表示组件上次渲染时花费的时间,颜色表示当前 commits(提交) 部分所花费的时间。)
    image.png

    可以通过单击组件放大或缩小火焰图。
  5. 排序图表(Ranked chart
    排序图视图表示单个 commit 。 图表中的每个横条代表一个 React 组件。 对图表进行排序,以便渲染时间最长的组件位于顶部。
    image.png

四. 监控工具

合成监控

  1. Lighthouse
    (1) chrome运行
    Lighthouse是直接集成到chrome开发者工具中的,位于Audits面板下
    image.png

    image.png

    (2) 本地运行
~ npm i -g lighthouse
~ lighthouse http://secure.finedevelop.com:65081/webroot/decision/url/mobile
  1. PageSpeed
    https://developers.google.com/speed/pagespeed/insights/
    image.png
  2. WebPageTest
    https://www.webpagetest.org/
    image.png

image.png

注释:关注Start RenderFirst Contentful PaintSpeed Index等性能指标。

真实用户监控

参考资料

如何进行 web 性能监控?
RAIL,以用户为核心的性能模型
Performance API
全新Chrome Devtools Performance使用指南
React性能测量和分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容