🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 官方指引 https://web.dev/ # 监控指标 ![](https://img.kancloud.cn/83/00/83009f11785987b241ea09b4782d3bf2_1764x1023.png) ![](https://img.kancloud.cn/3d/82/3d824c8b9eb3f93ff57a3f8d094d21b9_1555x943.png) <br> <br> 首先我们需要知道应该监控些什么呢?有哪些具体的指标? google 开发者提出了一种 RAIL 模型来衡量应用性能,即:Response、Animation、Idle、Load,分别代表着 web 应用生命周期的四个不同方面。并指出最好的性能指标是:100ms 内响应用户输入;动画或者滚动需在 10ms 内产生下一帧;最大化空闲时间;页面加载时长不超过 5 秒。 [![0](https://tva1.sinaimg.cn/large/006tNbRwgy1gah4zxc779j314w0u07bg.jpg)](https://tva1.sinaimg.cn/large/006tNbRwgy1gah4zxc779j314w0u07bg.jpg) 我们可转化为三个方面来看:响应速度、页面稳定性、外部服务调用 响应速度:页面初始访问速度 + 交互响应速度 页面稳定性:页面出错率 外部服务调用:网络请求访问速度 ## 页面访问速度:白屏、首屏时间、可交互时间 我们来看看 google 开发者针对用户体验,提出的几个性能指标 [![1](https://tva1.sinaimg.cn/large/006tNbRwgy1gah51k3lg8j31bi0gg45d.jpg)](https://tva1.sinaimg.cn/large/006tNbRwgy1gah51k3lg8j31bi0gg45d.jpg) <br> 这几个指标其实都是根据用户体验,提炼出对应的性能指标 [![](https://tva1.sinaimg.cn/large/006tNbRwgy1gah52b9pkrj30v00bwabn.jpg)](https://tva1.sinaimg.cn/large/006tNbRwgy1gah52b9pkrj30v00bwabn.jpg) ### first paint (FP) and first contentful paint (FCP) 首次渲染、首次有内容的渲染 这两个指标浏览器已经标准化了,从 performance 的 The Paint Timing API 可以获取到,一般来说两个时间相同,但也有情况下两者不同。 * PerformanceObserver 在页面html代码顶部加入以下代码,订阅性能事件。 ~~~ const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { const metricName = entry.name; const time = Math.round(entry.startTime + entry.duration); console.log(entry); console.log(metricName + ' : ' + time); } }); observer.observe({entryTypes: ['paint']}); ~~~ 上述代码的输出如下: ![](https://img.kancloud.cn/11/8e/118e3da1d4500bd601435dfe34a10db4_732x94.png) * performance 下面的代码也是可以的: ~~~ window.performance.getEntriesByType('paint'); ~~~ ![](https://img.kancloud.cn/80/bc/80bce3c87799164a4d7da3211c2a8d5a_732x111.png) <br> <br> ### First meaningful paint (FMP) 首次有效绘制。这是一个很主观的指标。根据业务的不同,每一个网站的有效内容都是不相同的,有效内容就是网页中"主角元素"。对于视频网站而言,主角元素就是视频。对于搜索引擎而言,主角元素就是搜索框。 <br> google lighthouse 定义的 first meaningful paint: [https://docs.google.com/document/d/1BR94tJdZLsin5poeet0XoTW60M0SjvOJQttKT-JK8HI/view](https://docs.google.com/document/d/1BR94tJdZLsin5poeet0XoTW60M0SjvOJQttKT-JK8HI/view) <br> <br> ### Time to interactive(TTI) 可交互时间。用于标记应用已进行视觉渲染并能可靠响应用户输入的时间点。应用可能会因为多种原因而无法响应用户输入: ①页面组件运行所需的JavaScript尚未加载完成。 ②耗时较长的任务阻塞主线程 <br> TTI也是一个较为主观的指标。浏览器并没有为这个指标提供api。不过google提供了polyfill。下面是使用方法。 在页面html顶部加入: ~~~ !function(){if('PerformanceLongTaskTiming' in window){var g=window.__tti={e:[]}; g.o=new PerformanceObserver(function(l){g.e=g.e.concat(l.getEntries())}); g.o.observe({entryTypes:['longtask']})}}(); ~~~ install这个polyfill, ~~~ npm install tti-polyfill ~~~ 在业务代码中引用: ~~~ import ttiPolyfill from 'tti-polyfill'; ... ttiPolyfill.getFirstConsistentlyInteractive().then((tti) => { console.log('tti', tti); }); ... ~~~ > `getFirstConsistentlyInteractive()`方法接受可选的`startTime`配置选项,让您可以指定下限值(您知道您的应用在此之前无法进行交互)。 默认情况下,该 polyfill 使用 DOMContentLoaded 作为开始时间,但通常情况下,使用主角元素呈现的时刻或您知道所有事件侦听器都已添加的时间点这类时间会更准确。 <br> <br> ### 长任务 浏览器是单线程的,如果长任务过多,那必然会影响着用户响应时长。好的应用需要最大化空闲时间,以保证能最快响应用户的输入。 [![](https://tva1.sinaimg.cn/large/006tNbRwgy1gah53416gmj31bg0kgn9i.jpg)](https://tva1.sinaimg.cn/large/006tNbRwgy1gah53416gmj31bg0kgn9i.jpg) ### 页面稳定性:页面出错情况 资源加载错误 JS 执行报错 ### 外部服务调用 CGI 耗时 CGI 成功率 CDN 资源耗时 ## performance API 应用 <br> [performance API详解](https://www.kancloud.cn/surahe/front-end-notebook/1304833) <br> <br> 基于 performance 我们可以测量如下几个方面: mark、measure、navigation、resource、paint、frame。 <br> ~~~ let p = window.performance.getEntries(); 重定向次数:performance.navigation.redirectCount JS 资源数量:p.filter(ele => ele.initiatorType === "script").length CSS 资源数量:p.filter(ele => ele.initiatorType === "css").length AJAX 请求数量:p.filter(ele => ele.initiatorType === "xmlhttprequest").length IMG 资源数量:p.filter(ele => ele.initiatorType === "img").length 总资源数量: window.performance.getEntriesByType("resource").length ~~~ <br> **不重复的耗时时段区分:** ~~~ 重定向耗时: redirectEnd - redirectStart DNS 解析耗时: domainLookupEnd - domainLookupStart TCP 连接耗时: connectEnd - connectStart SSL 安全连接耗时: connectEnd - secureConnectionStart 网络请求耗时 (TTFB): responseStart - requestStart HTML 下载耗时:responseEnd - responseStart DOM 解析耗时: domInteractive - responseEnd 资源加载耗时: loadEventStart - domContentLoadedEventEnd ~~~ <br> **其他组合分析:** ~~~ 白屏时间: domLoading - fetchStart 粗略首屏时间: loadEventEnd - fetchStart 或者 domInteractive - fetchStart DOM Ready 时间: domContentLoadEventEnd - fetchStart 页面完全加载时间: loadEventStart - fetchStart ~~~ <br> **JS 总加载耗时:** ~~~ const p = window.performance.getEntries();let cssR = p.filter(ele =&gt; ele.initiatorType === "script");Math.max(...cssR.map((ele) =&gt; ele.responseEnd)) - Math.min(...cssR.map((ele) =&gt; ele.startTime)); ~~~ <br> **CSS 总加载耗时:** ~~~ const p = window.performance.getEntries();let cssR = p.filter(ele =&gt; ele.initiatorType === "css");Math.max(...cssR.map((ele) =&gt; ele.responseEnd)) - Math.min(...cssR.map((ele) =&gt; ele.startTime)); ~~~ <br> # 如何监控? 在了解了 performance 之后,我们来看看,具体是如何监控的? <br> [![](https://tva1.sinaimg.cn/large/006tNbRwgy1gah5jbytugj31mq0b2q50.jpg)](https://tva1.sinaimg.cn/large/006tNbRwgy1gah5jbytugj31mq0b2q50.jpg) <br> 总体流程:性能指标收集与数据上报—数据存储—数据聚合—分析展示—告警、报表推送 <br> 这里主要讲述如何收集性能数据。 性能指标收集注意项:1)保证数据的准确性 2)尽量不影响应用的性能 <br> ## 基本性能上报 采集数据:将 performance navagation timing 中的所有点都上报,其余的上报内容可参考 performance 分析一节中截取部分上报。例如:白屏时间,JS 和 CSS 总数,以及加载总时长。 其余可参考的上报:是否有缓存?是否启用 gzip 压缩、页面加载方式。 在收集好性能数据后,即可将数据上报。 那选择什么时机上报? google 开发者推荐的上报方式: [![](https://tva1.sinaimg.cn/large/006tNbRwgy1gah5jncmspj31fw0lgtct.jpg)](https://tva1.sinaimg.cn/large/006tNbRwgy1gah5jncmspj31fw0lgtct.jpg) <br> ## 首屏时间计算 我们知道首屏时间是一项重要指标,但是又很难从 performance 中拿到,来看下首屏时间计算主要有哪些方式? [https://web.dev/first-meaningful-paint/](https://web.dev/first-meaningful-paint/) 1)用户自定义打点—最准确的方式(只有用户自己最清楚,什么样的时间才算是首屏加载完成) 2)lighthouse 中使用的是 chrome 渲染过程中记录的 trace event 3)可利用[Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/)拿到页面布局节点数目。思想是:获取到当页面具有最大布局变化的时间点 4)aegis 的方法:利用 MutationObserver 接口,监听 document 对象的节点变化。 检查这些变化的节点是否显示在首屏中,若这些节点在首屏中,那当前的时间点即为首屏渲染时间。但是还有首屏内图片的加载时间需要考虑,遍历 performance.getEntries() 拿到的所有图片实体对象,根据图片的初始加载时间和加载完成时间去更新首屏渲染时间。 5)利用 MutationObserver 接口提供了监视对 DOM 树所做更改的能力,是 DOM3 Events 规范的一部分。 方法:在首屏内容模块插入一个 div,利用 Mutation Observer API 监听该 div 的 dom 事件,判断该 div 的高度是否大于 0 或者大于指定值,如果大于了,就表示主要内容已经渲染出来,可计算首屏时间。 6)某个专利:在 loading 状态下循环判断当前页面高度是否大于屏幕高度,若大于,则获取到当前页面的屏幕图像,通过逐像素对比来判断页面渲染是否已满屏。[https://patentimages.storage.googleapis.com/bd/83/3d/f65775c31c7120/CN103324521A.pdf](https://patentimages.storage.googleapis.com/bd/83/3d/f65775c31c7120/CN103324521A.pdf) 7) 引入html2canvas,递归截图并计算9个点的颜色红色通道像素值,前后数据相同,可以上报时间。[如何计算首屏加载时间?](https://www.cnblogs.com/caizhenbo/p/7993533.html) <br> <br> [![](https://tva1.sinaimg.cn/large/006tNbRwgy1gah5jysgr5j31et0u0h1a.jpg)](https://tva1.sinaimg.cn/large/006tNbRwgy1gah5jysgr5j31et0u0h1a.jpg) <br> ## 异常上报 1)js error 监听 window.onerror 事件 2)promise reject 的异常 监听 unhandledrejection 事件 ~~~ window.addEventListener("unhandledrejection", function (event) { console.warn("WARNING: Unhandled promise rejection. Shame on you! Reason: " + event.reason); }); ~~~ 3)资源加载失败 window.addEventListener('error') 4)网络请求失败 重写 window.XMLHttpRequest 和 window.fetch 捕获请求错误 5)iframe 异常 window.frames\[0\].onerror 6)window.console.error ## CGI 上报 大致原理:拦截 ajax 请求 数据存储与聚合 一个用户访问,可能会上报几十条数据,每条数据都是多维度的。即:当前访问时间、平台、网络、ip 等。这些一条条的数据都会被存储到数据库中,然后通过数据分析与聚合,提炼出有意义的数据。例如:某日所有用户的平均访问时长、pv 等。 数据统计分析的方法:平均值统计法、百分位数统计法、样本分布统计法。 # 参考资料 [如何进行 web 性能监控?](http://www.alloyteam.com/2020/01/14184/#prettyPhoto) [当考虑网页首屏加速的时候,我们在考虑什么](https://segmentfault.com/a/1190000018990133?utm_medium=referral&utm_source=tuicool) [漫谈前端性能优化](https://juejin.im/post/5a4f09eef265da3e3b7a5399) [研究首屏时间?你先要知道这几点细节](http://www.alloyteam.com/2016/01/points-about-resource-loading/)