当前位置: 首页 > news >正文

如何进行页面前端监控

🧑‍💻 写在开头
点赞 + 收藏 === 学会🤣🤣🤣

前端监控主要分三个方向

前端性能(用户体验优化)
异常监控
业务指标跟

下面我来分别介绍三类指标如何获取

1)前端性能指标:

一、用户体验相关的:

页面加载时间(Page Load Time) :

定义:从用户请求页面到页面完全加载的时间。
测量方法:使用Performance API中的performance.timing对象。

首次内容绘制(First Contentful Paint, FCP) :

定义:浏览器从DOM中渲染出第一个内容的时间点。
测量方法:使用PerformanceObserver来监听paint条目。

最大内容绘制(Largest Contentful Paint, LCP) :

定义:页面加载过程中最大可见内容的绘制时间。
测量方法:使用PerformanceObserver来监听largest-contentful-paint条目。

首次输入延迟(First Input Delay, FID) :

定义:用户首次与页面交互(如点击按钮)到浏览器开始响应交互的时间。
测量方法:使用EventListeners结合PerformanceObserver。

累积布局偏移(Cumulative Layout Shift, CLS) :

定义:页面布局在加载过程中发生的意外变化的累积得分。
测量方法:使用PerformanceObserver来监听layout-shift条目。

总阻塞时间(Total Blocking Time, TBT):

定义: TBT 是一个网页性能指标,用于衡量从首次内容绘制(First Contentful Paint, FCP)到可交互时间(Time to Interactive, TTI)之间,所有超过 50 毫秒的长任务阻塞主线程的时间总和。TBT 反映了页面在加载过程中用户交互的延迟情况。
测量方法: 使用PerformanceObserver来监听longtask条目。通过这种方式,可以捕获并计算页面在加载过程中所有长任务的阻塞时间。

交互时间(Time to Interactive, TTI) :

定义:页面从开始加载到完全可交互的时间。
测量方法:通常通过分析资源加载和长任务来估算。虽然没有直接的API,但可以结合Long Tasks API和其他指标来推测。

示例代码
以下是一个简单的示例代码,展示如何使用Performance API和PerformanceObserver来测量这些指标:

// 页面加载时间
window.addEventListener('load', () => {const timing = performance.timing;const pageLoadTime = timing.loadEventEnd - timing.navigationStart;console.log(`Page Load Time: ${pageLoadTime} ms`);
});// 首次内容绘制和最大内容绘制
const observer = new PerformanceObserver((list) => {const entries = list.getEntries();entries.forEach((entry) => {if (entry.name === 'first-contentful-paint') {console.log(`First Contentful Paint: ${entry.startTime} ms`);}if (entry.entryType === 'largest-contentful-paint') {console.log(`Largest Contentful Paint: ${entry.startTime} ms`);}});
});
observer.observe({ type: 'paint', buffered: true });
observer.observe({ type: 'largest-contentful-paint', buffered: true });// 首次输入延迟
let firstInputDelay = 0;
const fidObserver = new PerformanceObserver((list) => {const entries = list.getEntries();for (const entry of entries) {firstInputDelay = entry.processingStart - entry.startTime;console.log(`First Input Delay: ${firstInputDelay} ms`);}
});
fidObserver.observe({ type: 'first-input', buffered: true });// 累积布局偏移
let cumulativeLayoutShiftScore = 0;
const clsObserver = new PerformanceObserver((list) => {for (const entry of list.getEntries()) {if (!entry.hadRecentInput) {cumulativeLayoutShiftScore += entry.value;console.log(`Cumulative Layout Shift: ${cumulativeLayoutShiftScore}`);}}
});
clsObserver.observe({ type: 'layout-shift', buffered: true });// 长任务
const ttiObserver = new PerformanceObserver((entryList) => {const entries = entryList.getEntries();entries.forEach(entry => {console.log(`Long Task detected: ${entry.startTime} ms, duration: ${entry.duration} ms`);});
});ttiObserver.observe({ entryTypes: ['longtask'] });

总结:上面的指标performance.timing主要是采集一些基础的时间,目前都是前后端分离的项目参考意义已经不大,除非是ssr和服务端的项目这部分指标有一些参考价值。像最大内容绘制largest-contentful-paint 是一个持续变动的指标,最后计算出最大时间,位移数据也是如此,长任务也是如此持续监听过程。

二、页面资源相关

在前端监控中资源监控方面,通常使用的是浏览器提供的Performance API。这个 API 提供了一组接口,对于资源监控,具体来说,可以使用以下接口:

PerformanceResourceTiming:

这个接口提供了关于每个资源加载的详细计时信息。通过它,你可以获取到资源加载的各个阶段的时间,例如 DNS 查询时间、TCP 连接时间、请求时间、响应时间等。

PerformanceObserver:

这是一个更为通用的接口,可以用来监听各种性能条目(performance entry)。通过PerformanceObserver,你可以监听到资源加载事件(resource类型),并在资源加载完成时获取到相应的PerformanceResourceTiming对象。

以下是一个简单的示例,展示如何使用PerformanceObserver来监控资源加载:

if ('PerformanceObserver' in window) {const observer = new PerformanceObserver((list) => {const entries = list.getEntriesByType('resource');entries.forEach((entry) => {console.log(`Resource: ${entry.name}`);console.log(`Start Time: ${entry.startTime}`);console.log(`Duration: ${entry.duration}`);console.log(`Initiator Type: ${entry.initiatorType}`);});});observer.observe({ type: 'resource', buffered: true });
}

总结:他可以对页面所有的资源加载进行监控,比如 js,css,图片还能够对xmlhttprequest和fetch监控。也就是说他能够对接口进行监控,当我们的页面有一些超长的请求可以单独识别出来进行报警。

下面是对请求接口的监控:

if ('PerformanceObserver' in window) {const observer = new PerformanceObserver((list) => {const entries = list.getEntriesByType('resource');entries.forEach((entry) => {if (entry.initiatorType === 'xmlhttprequest' || entry.initiatorType === 'fetch') {console.log(`API Request: ${entry.name}`);console.log(`Start Time: ${entry.startTime}`);console.log(`Duration: ${entry.duration}`);console.log(`Fetch Start: ${entry.fetchStart}`);console.log(`Response End: ${entry.responseEnd}`);console.log(`Transfer Size: ${entry.transferSize}`);}});});observer.observe({ type: 'resource', buffered: true });
}

三、其他一些不太重要的监控(不细讲)

navigator.getBattery() : 电池监控
Performance Memory API: 内存监控

2)异常监控

一、监听形式

监听形式主要是通过捕获全局错误事件来实现。这种方式可以捕获大部分未处理的异常,并可以将其记录或发送到服务器进行分析。

1. 使用window.onerror

window.onerror是最传统的异常捕获方法,可以捕获运行时的 JavaScript 错误。

window.onerror = function(message, source, lineno, colno, error) {console.error('Error caught:', message, source, lineno, colno, error);// 可以在这里将错误信息发送到服务器sendErrorToServer({message,source,lineno,colno,error: error ? error.stack : null});
};function sendErrorToServer(errorInfo) {fetch('/log-error', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify(errorInfo)});
}

2. 使用window.addEventListener(‘error’)

这种方法可以捕获资源加载错误(如图片、脚本加载失败)。

window.addEventListener('error', function(event) {console.error('Resource Error:', event);// 处理资源加载错误if (event.target instanceof HTMLImageElement) {console.error('Image failed to load:', event.target.src);}// 可以在这里将错误信息发送到服务器sendErrorToServer({message: event.message,source: event.filename,lineno: event.lineno,colno: event.colno,error: event.error ? event.error.stack : null});
}, true);

3. 使用window.addEventListener(‘unhandledrejection’)

捕获未处理的 Promise 异常,此处主要用来捕获Promise没有被catch的异常,属于最后的兜底。

window.addEventListener('unhandledrejection', function(event) {console.error('Unhandled promise rejection:', event.reason);// 可以在这里将错误信息发送到服务器sendErrorToServer({message: event.reason ? event.reason.toString() : 'Unknown reason',error: event.reason && event.reason.stack ? event.reason.stack : null});
});

二、主动上报形式

主动上报形式是指在代码中手动捕获异常并上报。适用于需要在特定逻辑中捕获异常的场景。

1. 使用try…catch

在代码中使用try…catch来捕获异常,并在catch块中进行上报。

function riskyOperation() {try {// 执行可能抛出异常的代码let result = potentiallyFailingFunction();console.log('Operation successful:', result);} catch (error) {console.error('Caught an error:', error);// 在这里上报错误sendErrorToServer({message: error.message,error: error.stack});}
}function potentiallyFailingFunction() {// 模拟可能抛出异常的代码if (Math.random() > 0.5) {throw new Error('Random failure');}return 'Success';
}

2. 集成到应用框架

在现代前端框架(如 React、Vue)中,可以利用框架的错误边界或错误处理机制来捕获和上报异常。

React: 使用 Error Boundary

class ErrorBoundary extends React.Component {constructor(props) {super(props);this.state = { hasError: false };}static getDerivedStateFromError(error) {return { hasError: true };}componentDidCatch(error, errorInfo) {console.error('ErrorBoundary caught an error:', error, errorInfo);// 在这里上报错误sendErrorToServer({message: error.message,error: error.stack,componentStack: errorInfo.componentStack});}render() {if (this.state.hasError) {return <h1>Something went wrong.</h1>;}return this.props.children;}
}// 使用 ErrorBoundary 包裹组件
<ErrorBoundary><YourComponent />
</ErrorBoundary>

总结:我本人更加倾向于主动上报形式,因为在现在的开发形式里面,编译工具和开发工具能够帮我们避免绝大多数的异常情况,我们的监控往往因为业务逻辑和接口数据返回不对导致对业务的影响,所以主动上报能够更加准确和清晰的给出异常,这样利于我们去监控,往往比较多的异常问题不是错误导致更多就是接口不符合预期这样window.onerror形式不太全,最多只能当一个辅助使用。

3、业务指标跟踪

业务指标跟踪也是前端监控的一部分,主要用于收集和分析用户在应用中的行为数据,以帮助理解用户的使用模式、评估功能效果、优化用户体验等。常见的业务指标包括页面访问量、点击事件、用户停留时间、转化率等,很多公司都是单独做,我所在的公司就是这样,其实原理很简单就是调用一个接口把想要上报的数据给带过去就行。

let clickCount = 0;// 获取按钮元素
const button = document.getElementById('trackButton');// 为按钮添加点击事件监听器
button.addEventListener('click', () => {clickCount++;console.log('按钮点击次数:', clickCount);
});// 页面卸载时发送数据
window.addEventListener('beforeunload', () => {const data = JSON.stringify({ clickCount });// 使用 sendBeacon 发送数据,确保在页面卸载时也能发送成功if (navigator.sendBeacon) {navigator.sendBeacon('/track-clicks', data);} else {// 备用方案,使用 fetch 发送数据fetch('/track-clicks', {method: 'POST',headers: {'Content-Type': 'application/json'},body: data});}
});

有个问题,为什么用navigator.sendBeacon去发送数据 ?这就涉及到如何上报数据的问题,下面我具体讲下。

一、五种数据上报的形式

XMLHttpRequest:经典的 AJAX 请求方式,用于发送数据到服务器。可以配置为同步或异步请求,但在页面卸载时可能不可靠。
Fetch API:现代浏览器中推荐的方式,用于发送网络请求。相比XMLHttpRequest,它提供了更简洁的语法,但在页面卸载时仍可能会被中断。
Navigator.sendBeacon:专门用于在页面卸载时发送数据的 API。sendBeacon是非阻塞的,确保数据在页面关闭时也能可靠地发送。
Image Ping:通过动态创建图像对象并设置其src属性来发送 GET 请求。这种方法常用于简单的数据上报,不会受到跨域限制,但只能发送少量数据。
WebSocket:用于建立持久连接,实现双向通信。适合需要实时数据传输的场景,但对于简单的数据上报来说可能过于复杂。

一共有5种形式上报,我来分析下各有什么优劣势:
其中可以XMLHttpRequest和Fetch没有什么太大本质的区别属于一类,他们都是比较常规的上报形式,但是他有个致命问题可能会被中断,导致数据丢失,特别在页面跳出的情况,还有就是在请求里面优先级高,在页面加载的前期当有大量的上报的时候会阻塞页面的请求看下图:
在这里插入图片描述

Image Ping图片上报,在跨域和调用方式上比较好,但是有个致命的缺点数据量带不多。
WebSocket我见过的很少,可能在某些场景用的多,但是我很少见到
Navigator.sendBeacon专为页面卸载设计,确保数据在页面关闭时发送。非阻塞:不会影响页面卸载速度。在上图里面的优先级属于低,使用简单,但是有个问题,兼容性有点问题,可以配合fetch使用。他不会应为页面切换丢数据,调用起来非常方便。

二、上报时机

数据上报的形式可以从不同的角度来分类和理解。一般来说,数据上报主要有以下几种形式:

实时上报:
数据在产生后立即发送到服务器。这种方式确保数据的实时性,适用于需要即时分析和处理的场景。

批量上报:
数据在客户端累积到一定数量或满足特定条件后,再一次性发送到服务器。这种方式可以减少网络请求次数,适用于对时效性要求不高的场景。

定时上报:
客户端在固定的时间间隔内发送数据到服务器。这种方式可以平衡实时性和网络资源的消耗。

页面卸载上报: 在页面关闭或卸载时发送数据,通常使用sendBeaconAPI。这种方式确保在用户离开页面时也能可靠地将数据发送到服务器。

事件驱动上报:
基于特定事件触发数据上报,例如用户点击按钮、表单提交等。这种方式适用于需要对特定用户行为进行监控的场景。

一共有5种形式时机,我来分析下各有什么优劣势:

实时上报:
优点:
实时性:数据在产生后立即发送,适合需要即时分析的场景。
准确性:可以立即捕捉用户行为,减少数据丢失的风险。
缺点:
网络开销:频繁发送请求可能增加网络负担。
服务器压力:需要服务器能够处理高频率的数据请求。

批量上报:
优点:
减少请求次数:通过合并多条数据减少网络请求。
提高效率:降低网络和服务器的负担。
缺点:
时效性:数据不是实时发送,可能导致分析滞后。
数据丢失风险:如果客户端崩溃或断网,未发送的数据可能丢失。

定时上报:
优点:
平衡实时性和效率:通过定时发送,减少频繁请求。
可控性:可以根据需求调整上报频率。
缺点:
复杂性:需要实现定时器逻辑。
数据可能不够实时:与实时上报相比,数据可能有延迟。

页面卸载上报:
优点:
可靠性:利用sendBeacon确保数据在页面关闭时发送。
简单性:不需要复杂的逻辑,只需在页面卸载时触发。
缺点:
数据量限制:通常只能发送较小的数据量。
依赖浏览器支持:如果浏览器不支持sendBeacon,需要备用方案。

事件驱动上报:
优点:
精确性:针对特定用户行为进行上报,适合精准分析。
灵活性:可以根据业务需求定制上报逻辑。
缺点:
复杂性:需要为每个事件设置监听器和上报逻辑。
潜在的性能问题:过多的事件监听可能影响页面性能。

看你业务需要那种形式,去评估那种形式上报。

我来总结下我在公司遇到的几种情况:公司H5目前用的最多的就是第一种,但是在客户端里面是批量,我理解客户端的形式是最好的,H5的页面如果在端里面可以借助端的能力去实现批量上报。我们也可以在h5里面页面卸载的时候去上报,我自己测试过好像有一些兼容问题,最后还是放弃了用的实时上报。

三、最后来个小问题,如何对你的页面程序方法运行时间进行监控?

页面程序方法的运行时间进行监控是性能优化的重要一环,可以帮助识别性能瓶颈和优化点。以下是一些常用的方法来监控方法的运行时间:

使用console.time和console.timeEnd:
这是最简单的方法之一,适用于开发和调试阶段。
使用console.time(‘label’)开始计时,console.timeEnd(‘label’)结束计时,并在控制台输出运行时间。

console.time('myFunction');myFunction();console.timeEnd('myFunction');

使用performance.now() :
performance.now()提供高精度的时间戳,可以用于精确测量函数的执行时间。

const start = performance.now();myFunction();const end = performance.now();console.log(`myFunction took ${end - start} milliseconds`);

使用PerformanceAPI:
浏览器的PerformanceAPI 提供了更全面的性能测量工具,可以记录和分析多个时间点。
可以使用performance.mark()和performance.measure()来标记和测量代码片段。

performance.mark('start');myFunction();
performance.mark('end');
performance.measure('myFunction', 'start', 'end');const measures = performance.getEntriesByName('myFunction');console.log(measures[0].duration);

使用第三方库:

有一些第三方库可以帮助监控和分析性能,如stats.js或New Relic等。
这些工具通常提供更详细的性能分析和报告功能。

自定义监控工具:

如果需要更复杂的监控,可以编写自定义的监控工具,结合performance.now()和日志记录,将数据发送到服务器进行分析。

目前用的比较多的是performance.mark()因为他是用的比new date提供高精度的时间戳,具体的可以自己去查文档,我懒得讲performance.mark()是 Web Performance API 提供的一个方法,用于在浏览器的性能时间线上创建一个标记。这个标记可以帮助开发者测量特定代码段的执行时间,尤其是在需要精确测量性能的复杂应用中。

用法
创建标记: 使用performance.mark(name)来创建一个标记,其中name是一个字符串,用于标识这个标记。例如:

performance.mark('start');// 执行一些代码
performance.mark('end');

测量时间: 使用performance.measure(name, startMark, endMark)方法来测量两个标记之间的时间间隔。name是测量的名称,startMark和endMark是之前创建的标记。例如:

performance.measure('myMeasure', 'start', 'end');

获取测量结果: 使用performance.getEntriesByName(name)来获取测量结果。这个方法返回一个数组,其中包含所有与指定名称匹配的测量结果。例如:

const measures = performance.getEntriesByName('myMeasure');
measures.forEach((measure) => {console.log(`${measure.name}: ${measure.duration}ms`);
});

我以前用过一个蠢方法就是用mark形式对接口进行打点监控,其实前面见过用资源监控就能完成,后面我为了精准的监控页面真实的渲染时间,用过mack在页面渲染完成的时间点去打mark,后面发现有lcp后放弃,但是我认为在有些特殊场景还是能用上,特别在性能优化到后期实在找不到优化点的时候可以对具体的某些方法进行监控优化。

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。
在这里插入图片描述

相关文章:

  • window 显示驱动开发-DirectX 视频加速 2.0
  • 如何用 pnpm patch 给 element-plus 打补丁修复线上 bug(以 2.4.4 修复 PR#15197 为例)
  • 资源-又在网上淘到金了-配乐下载
  • 8.RV1126-OPENCV 视频中添加LOGO
  • 实现对deepseek流式返回的json数据,进行逐字解析并实时渲染
  • Python中os模块详解
  • 蓝桥杯 k倍区间
  • [蓝桥杯]生物芯片
  • 负载均衡相关基本概念
  • 通过阿里云 DashScope API 调用通义千问
  • [蓝桥杯]求解台阶问题
  • Redis 缓存问题及其解决方案
  • DrissionPage 异常处理实战指南:构建稳健的网页自动化防线
  • Eureka 高可用集群搭建实战:服务注册与发现的底层原理与避坑指南
  • n8n 自动化平台 Docker 部署教程(附 PostgreSQL 与更新指南)
  • (13)java+ selenium->元素定位大法之By_partial_link_text
  • 04 APP 自动化- Appium toast 元素定位列表滑动
  • 内网横向之RDP缓存利用
  • Redis 缓存粒度如何控制?缓存整个对象还是部分字段?
  • Mac 芯片系列 安装cocoapod 教程
  • 2013网站设计/百度普通收录
  • 自己建一个网站/整合营销的特点有哪些
  • 常州网站建设企业网站制作/重庆森林为什么不能看
  • 济南网站建设公司/短视频seo系统
  • 南京高端网站建设/网络推广具体内容
  • 找别人建网站去哪里/百度云登录