本文已参与「新人创作礼」活动,一起开启掘金创作之路。详情
前端监控一般主要就是三种:页面埋点、性能监控、异常监控。
用户行为监控
页面埋点应该是最经常使用的监控方式,可以监听用户的行为,主要设计以下数据:
PV/UV(浏览量 / 不同IP地址的人数)- 停留时长
- 流量来源
- 用户交互
统计这些数据是有意义的,比如我们知道了用户来源的渠道,可以促进产品的推广,知道用户在每一个页面停留的时间,可以针对停留较长的页面,增加广告推送等等。
对于这几类统计,一般的实现思路大致可以分为两种:
-
手写埋点方式。
这一种是比较常用的方式,可以自主选择需要监控的数据然后在相应的地方写入代码。优点:灵活性大。缺点:工作量大,需要监控的地方都要插入埋点代码。
-
无埋点的方式。
这种无埋点的方式基本不需要开发者手写埋点了,而是统计所有的事件并且定时上报。这种方式虽然没有前一种方式繁琐了,但是因为统计的是所有事件,所以还需要后期过滤出需要的数据。
性能监控
性能监控指的是监听前端页面的性能,主要包括监听网页或者说产品在用户端的体验。
根据性能监控的结果可以优化产品。常见的性能监控数据包括:
- 不同用户,不同机型和不同系统下的首屏加载时间
- 白屏时间
- http等请求的响应时间
- 静态资源整体下载时间
- 页面渲染时间
- 页面交互动画完成时间
监控指标具体可以分类为:网络层面和页面展示层面。
-
从网络层面来看涉及的指标有:重定向耗时、DNS解析耗时、TCP连接耗时、SSL耗时、TTFB网络请求耗时、数据传输耗时、资源加载耗时等
-
页面展示层面的指标是针对用户体验提出的几个指标,包含FP、FCP、LCP、FMP、DCL、L等,这几个指标其实就是chrome浏览器中performance模块的指标(如图所示)。
性能监控怎么做?使用浏览器自带的 Performance API 就可以实现这个功能。
window.performance中的timing属性中的内容不就是为了求解上述指标所需要的值吗?看着上面的属性值再对应下面的performance访问流程图,整个过程是不是一目了然。
异常监控
前端错误分类
前端错误主要包含两类:
- 即时运行错误(代码错误)
- 资源加载错误
捕获方式
1、try...catch
try {
console.log('1->begin')
error
console.log('2->begin')
} catch (e) {
console.log('catch',e)
}
当使用try...catch后,控制台就不再爆红。错误被捕获
但是try catch对于异步的捕获就不生效了:
try {
setTimeout(() => {
console.log('1')
error
console.log('2')
})
} catch (e) {
console.log('catch', e)
}
上面这种情况是无法捕获错误的。
2、window.onerror
window.onerror函数。这个函数是全局的。同步异步的错误都可以捕获
window.onerror = function(msg, url, row, col, error) { ... }
// msg为异常基本信息
// url为发生异常`Javascript`文件的`url`
// row为发生错误的行号
function fun1() {
console.log('1->begin')
error
console.log('1->end')
}
window.onerror = (...args) => {
console.log('onerror:',args)
}
需要注意一个问题:onerror返回值:
如果返回返回true 就不会被上抛了。不然控制台中还会看到错误日志。
问题延伸1:
window.onerror默认无法捕获跨域的js运行错误。捕获出来的信息如下:(基本属于无效信息)
问题延申2:
window.onerror只能捕获即时运行错误,无法捕获资源加载错误。原理是:资源加载错误,并不会向上冒泡,object.onerror捕获后就会终止(不会冒泡给window),所以window.onerror并不能捕获资源加载错误。
3、监听error事件
源加载错误,虽然会阻止冒泡,但是不会阻止捕获。我们可以在捕获阶段绑定error事件。
window.addEventListener('error',() => {}
window.addEventListener('error', args => {
console.log(
'error event:', args
);
return true;
},
true // 利用捕获方式
);
资源加载错误问题
-
方式1:
object.onerror。img标签、script标签等节点都可以添加onerror事件,用来捕获资源加载的错误。 -
方式2:performance.getEntries。可以获取所有已加载资源的加载时长,通过这种方式,可以间接的拿到没有加载的资源错误。
// 已经成功加载的资源 performance.getEntries().forEach(item=>{console.log(item.name)}) // 需要加载的的img集合。 document.getElementsByTagName('img')于是,
document.getElementsByTagName('img')获取的资源数组减去通过performance.getEntries()获取的资源数组,剩下的就是没有成功加载的,这种方式可以间接捕获到资源加载错误。 -
方式3: Error事件捕获。
4、Promise和async/await异常捕获
Promise有catch的方法可以捕获自身的错误。但是每个Promise都协商catch显然不太可能。所以我们可以用unhandledrejection来统一处理
window.addEventListener("unhandledrejection", e => {
throw e.reason
});
async/await方法同理可以用unhandledrejection统一处理。
总结
| 异常类型 | 同步方法 | 异步方法 | 资源加载 | Promise | async/await |
|---|---|---|---|---|---|
| try/catch | ✔️ | ✔️ | |||
| onerror | ✔️ | ✔️ | |||
| error事件监听 | ✔️ | ✔️ | ✔️ | ||
| unhandledrejection事件监听 | ✔️ | ✔️ |
工程化项目中的异常捕获
跨域错误
Vue中
我们可以利用vue提供的handleError方法。一旦Vue发生异常都会调用这个方法。在src/main文件中定义错误捕获:
Vue.config.errorHandler = function (err, vm, info) {
console.log('errorHandle:', err)
}
React中
1、我们可以在src/index.js中利用error事件监听:
window.addEventListener('error', args => {
console.log('error', error)
})
2、ErrorBoundary标签
错误边界仅可以捕获其子组件的错误。错误边界无法捕获其自身的错误。如果一个错误边界无法渲染错误信息,则错误会向上冒泡至最接近的错误边界。这也类似于 JavaScript 中 catch {} 的工作机制。
① 创建ErrorBoundary组件
import React from 'react';
export default class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
}
componentDidCatch(error, info) {
// 发生异常时打印错误
console.log('componentDidCatch',error)
}
render() {
return this.props.children;
}
}
② 在src/index.js中包裹App标签
import ErrorBoundary from './ErrorBoundary'
ReactDOM.render(
<ErrorBoundary>
<App />
</ErrorBoundary>
, document.getElementById('root'));
异常上报方式
-
方式一:采用Ajax通信的方式上报
这和我们再业务程序中并没有什么区别。在这里就不赘述。(此方式虽然可以上报错误,但是我们并不采用这种方式)
-
方式二: 利用Image对象上报(推荐。网站的监控体系都是采用的这种方式)
方式二:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
//通过Image对象进行错误上报
(new Image()).src = 'http://blog.com/myPath?badjs=msg'; // myPath表示上报的路径(我要上报到哪里去)。后面的内容是自己加的参数。
</script>
</body>
</html>
这种方式,不需要借助第三方的库,一行代码即可搞定。
关于前端监控具体实现可以参考扩展: