监控分为非入侵式和入侵式,非入侵式比如开发者自己的开发者工具,入侵式则需要在项目中添加监控的代码,以便监控真实用户的数据。
一、 合成监控SYN--Synthetic Monitoring(非入侵式)
以chrome devTool 为例:
(一)Network中的timing
上述涵盖:连接开始(DNS解析、SSL)及请求和响应数据,其中TTFB反应服务器的响应速度。
各个解释可见:https://developers.google.com/web/tools/chrome-devtools/network/reference?utm_source=devtools#timing-explanation
(二)Performance
1、FP:first paint 首次绘制
a、定义
页面在导航后首次呈现出不同于导航前内容的时间点
2、FCP:first contentful paint 首个内容绘制时间
a、定义
当页面的任一部分首次在屏幕上渲染的时间点,这个内容包含文字、图片(含背景图)、svg元素或者非白色的canvas元素。下图中FCP出现在第二帧
b、如何改善FCP?
3、DCL:DOMContentLoaded Event
这个大家看名字应该就理解,略。
4、LCP:largest contentful paint 最大内容绘制时间
a、定义
视口内最大图片(不含
b、如何改善LCP?
关于改善LCP的方法详见:https://web.dev/optimize-lcp/ 如文中提到 尽早的建立第三方连接的方式是采用preconnect及dns-prefetch:
…
如何改进LCP?
5、FID first input delay 首次输入延迟
a、定义
衡量用户从点击链接、按钮等到浏览器实际能够访问之间的时间
b、如何改进FID?
详见: https://web.dev/optimize-fid/
6、TTI time to interactive 可交互时间
a、定义
从页面加载到页面主要子资源加载之间的时间,它能够快速可靠地响应用户输入。
b、如何改进TTI?
7、TBT total blocking time 总阻塞时间
a、定义
它度量了 首次内容绘制时间(FCP)和可交互时间(TTI)之间的总时间,在该时间中主线程被阻塞足够长的时间阻碍了输入的响应。
下图中主线程运行任务所花费的总时间为 250+90+35+30+155= 560ms,而总阻塞时间为 250-50+90-50+155-50=345ms
b、如何改进TBT?
8、CLS : cumulative layout shift 累计布局转移
a、定义
记录了页面上非预期的位移波动,其值为 位移影响的面积*位移距离
。如下图,原本文字元素高度占据了视口的一半,随后文字元素向下移动了视口高度的25%,则CLS的值为 75%(位移影响的面积) * 25%(位移距离)=0.1875
b、如何改善CLS?
9、L:onload event
a、定义 load 事件
以用户为中心的性能指标 https://web.dev/user-centric-performance-metrics/
(三)Lighthouse
当然这个也可以在网页上使用https://web.dev/measure/或者使用npm或插件,但是还是强烈推荐采用devTool的方式,因为它会针对不同指标给出很详细的优化建议。(网页版不稳定且无法给出详细建议,npm包不支持部分查询字符串的单页面项目)
主要有个5个指标:
1、performance 性能
如主要记录如首次内容绘制时间、可交互时间、总阻塞时间、最大内容绘制时间及累计布局转移时间等并进行打分。评分的由来 及计算器
我测试的详细分数
2、accessibilit 辅助功能
这个主要是检查 可能阻止用户访问你网站内容的常见问题。如背景和前景色的对比度是否足够、h1-h5标签的选用是否连贯合理、是否禁用了用户对网页的缩放功能
3、best practices 最佳实践
如 是否采用了https、引用的库是否有漏洞
4、seo 搜索引擎优化
如 确保 a链接可以被抓取,如下图写法就不可以被抓取
5、PWA
详见:https://web.dev/pwa-checklist/
(四)、其他第三方在线工具
1、https://gtmetrix.com/ (综合了pagespeed和ySlow)
2、https://www.webpagetest.org/
3、https://speedcurve.com/
二、真实用户监控 RUM--Real User Monitoring(入侵式)
上述是合成监控,还有真实用户监控:
1、sentry
接入步骤:
2、oneapm
3、阿里的arms
之前在前端错误监控系统汇总中提到
4、国外的NewRelic
5、汇总
| 国外
| NewRelic | https://newrelic.com/ |
| | Appdynamics | https://www.appdynamics.com/ |
| | Compuware | |
| | sentry | https://github.com/getsentry/sentry |
| | atatus | https://www.atatus.com/browser-monitoring/features
| | | |
| | | |
| 国内
| oneapm | https://browser.oneapm.com/#/browser |
| | 听云 | https://www.tingyun.com/ |
| | 监控宝 | https://www.jiankongbao.com/pageMnt.html |
| | 透视宝 | https://www.toushibao.com/product_browser.html |
| | 压测宝 | http://www.yacebao.com/product.html |
| | 阿里的arms | https://www.aliyun.com/product/arms |
| | mmtrix | https://www.mmtrix.com/ |
| | frontjs | https://www.frontjs.com/ |
三、实现原理
关于前端性能指标,W3C 定义了强大的 Performance API,其中又包括了 High Resolution Time 、 Frame Timing 、 Navigation Timing 、 Performance Timeline 、Resource Timing 、 User Timing 等诸多具体标准。
这里主要讲解 Navigation Timing 以及 Resource Timing。
(一)监控页面加载时间
const navTimes = performance.getEntriesByType('navigation')
const [{ domComplete }] = performance.getEntriesByType('navigation')
// performance.getEntriesByType('navigation')【定义了当前文档的导航信息,比如是重载还是向前向后等】 比
// performance.timing【定义了从 navigationStart 至 loadEventEnd 的 21 个只读属性】更精准
上图摘自w3c https://w3c.github.io/navigation-timing/
(二)资源加载
const [{ startTime, responseEnd }] = performance.getEntriesByType('resource')
const loadTime = responseEnd - startTime
上图摘自 https://www.w3.org/TR/resource-timing-2/
四、其他有趣的api
performance.getEntries() //对网页发起的所有HTTP请求耗时信息统计后,以数组方式返回
performance.now(); // 不受系统时间限制
performance.navigation.type //通过整数值表示网页从何加载
//0:网页通过点击链接、地址栏输入、表单提交、脚本操作等方式加载
//1:网页通过“重新加载”按钮或者location.reload()方法加载
//2:网页通过“前进”或“后退”按钮加载
//255:其他来源的加载
performance.navigation.redirectCount //页面重定向次数
推荐资料:https://zhuanlan.zhihu.com/p/82981365
https://zhuanlan.zhihu.com/p/400429457
六、其他
1、benchmark.js 基准性能测试
var Benchmark = require('benchmark');
var suite = new Benchmark.Suite;
suite
.add('indexOf', function() {
var fruits = ['apple', 'orange', 'grape'];
fruits.indexOf('mango'); // -1
})
.add('filter', function() {
var fruits = ['apple', 'orange', 'grape'];
fruits.filter(function(value) {
return value === 'mango';
});
})
.on('cycle', function(event) {
console.log(String(event.target));
})
.on('complete', function() {
console.log('Fastest is ' + this.filter('fastest').map('name'));
})
.run({ 'async': true });