前端监控
1: 你能得到的收益:
参与公司前端监控平台/监控SDK的架构设计和开发,为公司的业务赋能 独立设计和开发监控SDK,对前端页面的性能指标,错误信息 行为信息进行收集,采用合理的方式进行上报 课程内容:
涉及到的知识点 Rollup打包的配置 JavaScript基础知识 网络&浏览器相关知识 框架的错误收集(React Vue)
2:为什么要自研做监控?作用是什么?为什么不用第三方的监控平台,业界有那些成熟的监控平台
3:项目搭建
设计思路
一个完整的前端监控平台包括三个部分:数据采集与上报、数据分析和存储、数据展示 数据报警和监控

目录结构
4:性能监控的收集
1:页面性能检测工具:Lighthouse2:web-vitals:https://www.npmjs.com/package/web-vitals官方指标标准
| 指标 | 作用 | 标准 |
|---|---|---|
| FCP(First Contentful Paint) | 首次内容绘制时间 | 标准 ≤1s |
| LCP(Largest Contentful Paint) | 最大内容绘制时间 | 标准 ≤2 秒 |
| FID(first input delay) | 首次输入延迟,标准是用户触发后,到浏览器响应时间 | 标准 ≤100ms |
| CLS(Cumulative Layout Shift) | 累积布局偏移 | 标准 ≤0.1 |
| TTFB(Time to First Byte) | 页面发出请求,到接收第一个字节所花费的毫秒数(首字节时间) | 标准<= 100 毫秒 |
性能分析的,performance这个api的使用,和浏览器performance的定位 


监测网页加载时长时关注的是以下5个过程 :
- 重定向时间 :获取此网页前重定向所花费的时间
- DNS域名查找时间:查找此网页的 DNS 所花费的时间
- TCP服务器链接时间:用户连接到您的服务器所需的时间
- 服务器响应时间:您的服务器响应用户请求所需的时间,其中包括从用户所在位置连接到您的服务器所需的网络时间
- 网页下载时间:下载网页所需的时间
javascript
// window.performance.timing 各字段说明
{
navigationStart, // 同一个浏览器上下文中,上一个文档结束时的时间戳。如果没有上一个文档,这个值会和 fetchStart 相同。
unloadEventStart, // 上一个文档 unload 事件触发时的时间戳。如果没有上一个文档,为 0。
unloadEventEnd, // 上一个文档 unload 事件结束时的时间戳。如果没有上一个文档,为 0。
redirectStart, // 表示第一个 http 重定向开始时的时间戳。如果没有重定向或者有一个非同源的重定向,为 0。
redirectEnd, // 表示最后一个 http 重定向结束时的时间戳。如果没有重定向或者有一个非同源的重定向,为 0。
fetchStart, // 表示浏览器准备好使用 http 请求来获取文档的时间戳。这个时间点会在检查任何缓存之前。
domainLookupStart, // 域名查询开始的时间戳。如果使用了持久连接或者本地有缓存,这个值会和 fetchStart 相同。
domainLookupEnd, // 域名查询结束的时间戳。如果使用了持久连接或者本地有缓存,这个值会和 fetchStart 相同。
connectStart, // http 请求向服务器发送连接请求时的时间戳。如果使用了持久连接,这个值会和 fetchStart 相同。
connectEnd, // 浏览器和服务器之前建立连接的时间戳,所有握手和认证过程全部结束。如果使用了持久连接,这个值会和 fetchStart 相同。
secureConnectionStart, // 浏览器与服务器开始安全链接的握手时的时间戳。如果当前网页不要求安全连接,返回 0。
requestStart, // 浏览器向服务器发起 http 请求(或者读取本地缓存)时的时间戳,即获取 html 文档。
responseStart, // 浏览器从服务器接收到第一个字节时的时间戳。
responseEnd, // 浏览器从服务器接受到最后一个字节时的时间戳。
domLoading, // dom 结构开始解析的时间戳,document.readyState 的值为 loading。
domInteractive, // dom 结构解析结束,开始加载内嵌资源的时间戳,document.readyState 的状态为 interactive。
domContentLoadedEventStart, // DOMContentLoaded 事件触发时的时间戳,所有需要执行的脚本执行完毕。
domContentLoadedEventEnd, // DOMContentLoaded 事件结束时的时间戳
domComplete, // dom 文档完成解析的时间戳, document.readyState 的值为 complete。
loadEventStart, // load 事件触发的时间。
loadEventEnd; // load 时间结束时的时间。
}FP、FCP、LCP、CLS、TTFB、FID 官方指标标准
| 指标 | 作用 | 标准 |
|---|---|---|
| FCP(First Contentful Paint) | 首次内容绘制时间 | 标准 ≤1s |
| LCP(Largest Contentful Paint) | 最大内容绘制时间 | 标准 ≤2 秒 |
| FID(first input delay) | 首次输入延迟,标准是用户触发后,到浏览器响应时间 | 标准 ≤100ms |
| CLS(Cumulative Layout Shift) | 累积布局偏移 | 标准 ≤0.1 |
| TTFB(Time to First Byte) | 页面发出请求,到接收第一个字节所花费的毫秒数(首字节时间) | 标准<= 100 毫秒 |
5:错误监控的收集
- JS 代码运行错误、语法错误等
- 异步错误等
- 静态资源加载错误
- 接口请求报错
6:用户行为收集
javascript
// 创建用户行为类
class Behavior {
// maxBreadcrumbs控制上报用户行为的最大条数
maxBehaviors = 20;
// stack 存储用户行为
stack = [];
constructor() {}
// 添加用户行为栈
push(data) {
if (this.stack.length >= this.maxBehaviors) {
// 超出则删除第一条
this.stack.shift();
}
this.stack.push(data);
// 按照时间排序
this.stack.sort((a, b) => a.time - b.time);
}
}
let behavior = new Behavior();
// 添加一条页面跳转的行为,从home页面跳转到about页面
behavior.push({
type: "Route",
form: '/home',
to: '/about'
url: "http://localhost:9000/index.html",
time: "1668759320435"
});
// 添加一条用户点击行为
behavior.push({
type: "Click",
dom: "<button id='btn'>按钮</button>",
time: "1668759620485"
});
// 添加一条调用接口行为
behavior.push({
type: "Xhr",
url: "http://xxx.baidu.com/monitor/open/pushData",
time: "1668760485550"
});
// 上报用户行为
reportData({
uuid: "a6481683-6d2e-4bd8-bba1-64819d8cce8c",
stack: behavior.getStack()
});7:数据上报
8:测试
sdk打包
webpack rollup
javascript
@babel/preset-env
@rollup/plugin-babel
@rollup/plugin-json
rollup跨域问题
javascript
window.addEventListener(
'error',
(error) => {
console.log('捕获到异常:', error);
},
true
);
// 当前页面加载其他域的资源,如https://www.test.com/index.js
<script src="https://www.test.com/index.js"></script>;
// 加载的https://www.test.com/index.js的代码
function fn() {
JSON.parse('');
}
fn();报错信息
解决方法: 前端 script 加 crossorigin,后端配置 Access-Control-Allow-Origin
javascript
<script src="https://www.test.com/index.js" crossorigin></script>如果不能修改服务端的请求头,可以考虑通过使用 try/catch 绕过,将错误抛出
javascript
<!doctype html>
<html>
<body>
<script src="https://www.test.com/index.js"></script>
<script>
window.addEventListener("error", error => {
console.log("捕获到异常:", error);
}, true );
try {
// 调用https://www.test.com/index.js中定义的fn方法
fn();
} catch (e) {
throw e;
}
</script>
</body>
</html>其他



下面来介绍下上述字段的含义:
- navigationStart:表示上一个文档卸载结束时的unix时间戳,如果没有上一个文档,则等于fetchStart。
- unloadEventStart:表示前一个网页(与当前页面同域)unload的时间戳,如无前一个网页unloade或前一个网页与当前不同域,则为0。
- unloadEventEnd: 返回前一个unload时间绑定的回调执行完毕的时间戳。
- redirectStart:前一个Http重定向发送时的时间。有跳转且是同域名内重定向,否则为0。
- redirectEnd:前一个Http重定向完成时的时间。有跳转且是同域名内重定向,否则为0。
- fetchStart:浏览器准备使用http请求文档的时间,在检查本地缓存之前。
- domainLookupStart/domainLookupEnd:DNS域名查询开始/结束的时间,如果使用本地缓存(则无需DNC查询)或持久链接,则和fetchStart一致。
- connectStart:HTTP(TCP)开始或重新建立链接的时间,如果是持久链接,则和fetchStart一致。
- connectEnd:HTTP(TCP)完成建立链接的时间(完成握手),如果是持久链接,则和fetchStart一致。
- secureConnectionStart:Https链接开始的时间,如果不是安全链接则为0。
- requestStart:http在建立链接之后,正式开始请求真实文档的时间,包括从本地读取缓存。
- responseStart:http开始接收响应的时间(获取第一个字节),包括从本地读取缓存。
- responseEnd:http响应接收完全的时间(最后一个字节),包括从本地读取缓存。
- domLoading:开始解析渲染DOM树的时间。
- domInteractive:完成解析DOM树的时间。
- domContentLoadedEventStart:DOM解析完成后,页面内资源加载开始的时间。
- domContentLoadedEventEnd:DOM解析完成后,网页内资源加载完成的时间(如js脚本加载执行完)
- domComplete:DOM树解析完,资源也准备就绪。
- loadEventStart:load事件发送给文档,即load函数开始执行时。
- loadEventEnd:load函数执行完毕的时间。
前端性能核心指标(TTFB、FP、FCP、LCP、TTI、DCL)
市面上的前端监控: sentryhttps://sentry.io/welcome/
设计思路:
一个完整的前端监控平台包括三个部分: 数据采集与上报、数据分析和存储、数据展示
前端性能监控的工具
长任务: js执行事件大于50ms就是长任务
- web-vitals
- Lightouse
监控网页加载时长时关注的是一下5个过程:
- 重定向事件: 获取此网页前重定向所花费的时间
- DNS域名查找时间: 查找此网页的DNS
