前端性能监控

一.概述

门户入口,通过iframe集成其他内部项目,希望当用户打开项目(不管是门户或者iframe页)时,上报指标如
当前时间、访问Url、项目、页面加载耗时、超时资源等

主要目的是:

  1. 希望实现网络故障或繁忙时,自动预警
  2. 有针对性的优化用户访问速度,找到用户访问慢的根源
  3. 根据用户上报信息,对访问时段、访问频次、项目打开频次等数据做进一步的探索

二.大致步骤

1. 通过PerFormance对象获取性能信息

通过perFormance全局对象可以获取页面性能相关信息,目前兼容到IE10,已经可用

获取页面加载时间

1
2
3
performance.getEntriesByType('navigation')

通过name获取加载当前url , 根据domComplete获取页面加载耗(ms)

获取超时资源列表

1
2
3
4
5
performance.getEntriesByType('resource')
.filter(item => item.duration >= 5000) // 找出加载时间超过5s 的资源
// initiatorType 类型
// name 名称
// duration 耗时

2. 页面加载完自动上报

1
2
3
4
5
6
7
8
9
10
11
12
13
14

reportNavigate() {
// 后台上报
}

// 简略实现
window.onload = function (e) {
// 尽量不影响页面主线程
if (this.$window.requestIdleCallback) {
this.$window.requestIdleCallback(this.reportNavigate)
} else {
setTimeout(this.reportNavigate)
}
}

3. 监控iframe加载

通过传入iframe的window全局对象

三.完整代码

1
2
3
4
5
6
7
// 监控主项目,自动上报
new PerformanceMonitor('main')

// 监控iframe子项目, 手动上报
this.monitor = new PerformanceMonitor('iframe', window.frames[0].window, null, false)
// jsx
<iframe onLoad={() => {this.monitor.reportNavigate()}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

// 定义监控类
import { report } from '../services/monitor'
import moment from 'moment'

export default class PerformanceMonitor {

SECOND = 1000

/**
* origin 标示来源,当有多个日志源时
* $window 默认为window对象,iframe传入iframe的全局window对象
* timeout 超时时间判断
* auto 默认自动上报,若为false,需手动调用上报方法
*/
constructor(origin, $window, timeout, auto = true) {
this.origin = origin
this.timeout = timeout || this.SECOND * 5
this.$window = $window || window
this.reportNavigate = this.reportNavigate.bind(this)
// 自动上报
if (auto) {
this.bindNavigate()
}
}

// 获取页面加载时间
getLoadTime() {
const navigation = this.$window.performance.getEntriesByType('navigation')
if (navigation && navigation.length !== 0) {
return navigation[0].domComplete
} else {
// 兼容低版本Chrome
return this.$window.performance.timing.domComplete - this.$window.performance.timing.navigationStart
}
}

// 获取超时资源
getTimeoutRes() {
// initiatorType 类型
// name 名称
// duration 耗时
const resourceTimes = this.$window.performance.getEntriesByType('resource')
return resourceTimes
.filter(item => item.duration >= this.timeout)
.map(res => ({
TIMEOUTRES_TYPE: res.initiatorType || 'null',
TIMEOUTRES_URL: res.name,
TIMEOUTRES_DATE: res.duration
}))
}

// 获取当前URl并解码
getUrl() {
let url
try {
url = decodeURIComponent(this.$window.location.href)
} catch (error) {
console.log('url decode异常')
url = this.$window.location.href
}
return url
}

// 页面加载完上报
reportNavigate() {
const domComplete = this.getLoadTime()
const timeoutRes = this.getTimeoutRes()
const url = this.getUrl()
const logData = {
MONITOR_ORIGIN: this.origin,
MONITOR_TYPE: 'navigate',
MONITOR_URL: url,
MONITOR_TIMEOUT: this.timeout,
MONITOR_TIMEOUTRES: timeoutRes,
MONITOR_LOADTIME: domComplete,
MONITOR_REPORTTIME: moment().format('YYYY-MM-DD HH:mm:ss')
}
this.report(logData)
}

// 可以自定义上报信息
report(data) {
try {
report(data)
} catch (error) {
console.log('日志上报异常', error)
}
}

// 绑定事件、自动上报
bindNavigate() {
const oldOnload = this.$window.onload
this.$window.onload = function (e) {
if (oldOnload && typeof oldOnload === 'function') {
oldOnload(e)
}
// 尽量不影响页面主线程
if (this.$window.requestIdleCallback) {
this.$window.requestIdleCallback(this.reportNavigate)
} else {
setTimeout(this.reportNavigate)
}
}.bind(this)
}

}