前端性能监控

web性能优化
web性能 (w3)



A 前端监控

前端监控目标

稳定性

1)JS错误:执行错误&promise异常
1)资源错误:script/link等资源加载异常
1)接口错误 ajax/fetch请求接口异常

用户体验

1)加载时间
1)TTFB(首字节时间)
1)FP(首次绘制)
1)FCP(首次内容绘制)
1)FMP(首次有意义绘制)
1)FID(首次输入延迟):用户首次和页面交互到页面响应交互的时间
1)卡顿

业务

pv,uv,页面停留时间 等

前端监控流程

1)前端埋点
1)数据采集
1)数据建模存储
1)数据传输(实时/传输)
1)数据统计(分析/挖掘)
1)数据可视化反馈/报告和报警

RAIL测量模型

1)RAIL测量模型:Response 响应,给用户的响应体验、Animation 动画、Idle 空闲,主线程的空闲时间、Load 加载空闲
1)响应:处理事件应在50ms 以内完成
1)动画:每10ms产生一帧(60fps:一秒60帧,需考虑浏览器的绘制时间,大约6ms)
1)空闲:尽可能增加空闲时间
1)加载:在5s内完成内容加载并可以交互

B 错误捕获

1 脚本错误监控

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
// ### 常规脚本错误
// 1) try catch : 侵入式捕获错误,需在开发代码时在可能发生错误的代码片段中嵌入
// 2) error事件 捕获常规脚本错误
/**
* @description window.onerror 全局捕获错误
* @param event 错误信息,如果是
* @param source 错误源文件URL
* @param lineno 行号
* @param colno 列号
* @param error Error对象
*/
window.onerror = function (event, source, lineno, colno, error) {
// 上报错误
// 如果不想在控制台抛出错误,只需返回 true 即可
};

/**
* @param event 事件名
* @param function 回调函数
* @param useCapture 回调函数是否在捕获阶段执行,默认是false,在冒泡阶段执行
*/
window.addEventListener('error', (event) => {
// addEventListener 回调函数的离散参数全部聚合在 error 对象中
// 上报错误
}, true)


/* ### 2 Promise错误
当 Promise 被 reject 且没有 reject 处理器的时候,会触发 unhandledrejection 事件。
当 Promise 被 reject 且有 reject 处理器的时候,会触发 rejectionhandled 事件 */
// unhandledrejection 推荐处理方案
window.addEventListener('unhandledrejection', (event) => {
console.log(event)
}, true);


// rejectionhandled 推荐处理方案
window.addEventListener('rejectionhandled', (event) => {
console.log(event)
}, true);


// ### 3 框架错误
捕获vue框架错误
Vue.config.errorHandler = function (err, vm, info) {
// handle error
// `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
// 只在 2.2.0+ 可用
}

2 请求错误监控

axios: 在请求拦截器以及响应拦截器进行处理上报

3 资源错误监控

脚本错误参数对象 instanceof ErrorEvent
资源错误的参数对象 instanceof Event
ErrorEvent 继承于 Event
资源错误在捕获阶段被捕获

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @param event 事件名
* @param function 回调函数
* @param useCapture 回调函数是否在捕获阶段执行,默认是false,在冒泡阶段执行
*/
window.addEventListener('error', (event) => {
if (event instanceof ErrorEvent) {
console.log('脚本错误')
} else if (event instanceof Event) {
console.log('资源错误')
}
}, true);

4 跨域脚本错误捕获

在 script 标签中,添加 crossorigin 属性;同时,配置 CDN 服务器,为跨域脚本配上 CORS(存在兼容性问题,crossorigin 属性对于 IE 以及 Safari 支持程度不高)

22:12


2020.10.22 星期四 14:32

C 性能指标

1
2
3
var t;
var _performance = window.performance || window.msPerformance || window.webkitPerformance;
t = _performance.getEntriesByType('navigation')[0] || _performance.timing
指标 计算方式 描述
fcp perfEntries[key].startTime.toFixed(0) * 1 首次内容绘制
tcp t.connectEnd - t.connectStart TCP 建立连接完成握手的时间
dns t.domainLookupEnd - t.domainLookupStart 0 DNS 查询时间
ttfb t.responseStart - t.requestStart Time to First Byte,读取页面第一个字节的时间
trans t.responseEnd - t.responseStart 内容加载完成的时间
dom t.domInteractive - t.responseEnd
res t.loadEventStart - t.domContentLoadedEventEnd
ssl t.connectEnd - t.secureConnectionStart
firstbyte t.responseStart - t.domainLookupStart
fp var perfEntries = _performance.getEntries(); perfEntries[key].startTime.toFixed(0) * 1 > t.responseEnd - t.fetchStart 首次绘制
tti t.domInteractive - t.fetchStart
ready t.domContentLoadedEventEnd - t.fetchStart
load t.loadEventStart - t.fetchStart
effectiveType window.navigator.connection.effectiveType 网络类型(草案)
downlink window.navigator.connection.downlink 网络下行速度(草案)
rtt window.navigator.connection.rtt 估算的往返时间(草案)
platform window.navigator.platform 运行浏览器的操作系统和(或)硬件平台
rd 如下 重定向耗时

// rd 计算

1
2
3
4
5
6
7
if (t.navigationStart !== undefined) {
pData.rd = t.fetchStart - t.navigationStart
} else if (t.redirectEnd !== undefined) {
pData.rd = t.redirectEnd - t.redirectStart
} else {
pData.rd = 0
}

C_补充 1 performance 属性说明

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
  performance = {
    // memory 是非标准属性,只在 Chrome 有
    // 这个属性提供了一个可以获取到基本内存使用情况的对象
    memory: {
        usedJSHeapSize: 10000000, // JS 对象(包括V8引擎内部对象)占用的内存
        totalJSHeapSize: 10000000, // 可使用的内存
        jsHeapSizeLimit: 2190000000 // 内存大小限制
    },
    // navigation: 提供操作(包含在 timing 里时间)的有用上下文
    // 包括页面是加载还是刷新、发生了多少次重定向,等等。
    navigation: {
        redirectCount: 0, // 重定向次数
        type: 0 // 0 正常进入的页面(即非刷新、非重定向等)
        // 1 通过 window.location.reload() (即刷新页面)
        // 2 通过浏览器的前进后退按钮进入的页面(历史记录)
        // 255 非以上方式进入的页面
    },
    timing: {
        // 同一个浏览器上下文的上一个文档卸载(unload)结束时的UNIX时间戳。
        //如果没有上一个文档,这个值会和fetchStart相同。
        navigationStart: 1441112691935,
        // 上一个文档unload事件抛出时的UNIX时间戳。
        //如果没有上一个文档,这个值会返回0.
        unloadEventStart: 0,
        // 和 unloadEventStart 相对应,unload事件处理完成时的UNIX时间戳。
        //如果没有上一个文档,这个值会返回0.
        unloadEventEnd: 0,
        // 第一个HTTP重定向开始时的UNIX时间戳。
        //如果没有重定向,或者重定向中的一个不同源,这个值会返回0.
        redirectStart: 0,
        // 最后一个HTTP重定向完成时(也就是说是HTTP响应的最后一个比特直接被收到的时间)的UNIX时间戳。
        //如果没有重定向,或者重定向中的一个不同源,这个值会返回0.
        redirectEnd: 0,
        // 浏览器准备好使用HTTP请求来获取(fetch)文档的UNIX时间戳。这个时间点会在检查任何应用缓存之前。
        fetchStart: 1441112692155,
        // DNS 域名查询开始的UNIX时间戳。
        //如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和fetchStart一致。
       domainLookupStart: 1441112692155,
       // DNS 域名查询完成的时间.
       // 如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等
       domainLookupEnd: 1441112692155,
       // HTTP(TCP) 域名查询结束的UNIX时间戳。
       // 如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 fetchStart一致。
       connectStart: 1441112692155,
       // HTTP(TCP) 返回浏览器与服务器之间的连接建立时的Unix毫秒时间戳。
       // 如果建立的是持久连接,则返回值等同于fetchStart属性的值。
       // 连接建立指的是所有握手和认证过程全部结束。
       connectEnd: 1441112692155,
       // HTTPS 返回浏览器与服务器开始安全链接的握手时的Unix毫秒时间戳。
       // 如果当前网页不要求安全连接,则返回0。
       secureConnectionStart: 0,
       // 返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的Unix毫秒时间戳。
       requestStart: 1441112692158,
       // 返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix毫秒时间戳。
       // 如果传输层在开始请求之后失败并且连接被重开,该属性将会被数制成新的请求的相对应的发起时间。
       responseStart: 1441112692686,
       // 返回浏览器从服务器收到(或从本地缓存读取,或从本地资源读取)最后一个字节时(如果在此之前HTTP连接已经关闭,则返回关闭时)的Unix毫秒时间戳。
       responseEnd: 1441112692687,
       // 当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的 readystatechange事件触发时)的Unix毫秒时间戳。
       domLoading: 1441112692690,
       // 当前网页DOM结构结束解析、开始加载内嵌资源时(即Document.readyState属性变为“interactive”、相应的readystatechange事件触发时)的Unix毫秒时间戳。
       domInteractive: 1441112693093,
       // 当解析器发送DOMContentLoaded 事件,即所有需要被执行的脚本已经被解析时的Unix毫秒时间戳。
       domContentLoadedEventStart: 1441112693093,
       // 当所有需要立即执行的脚本已经被执行(不论执行顺序)时的Unix毫秒时间戳。
       domContentLoadedEventEnd: 1441112693101,
       // 当前文档解析完成,即Document.readyState 变为 'complete'且相对应的readystatechange 被触发时的Unix毫秒时间戳
       domComplete: 1441112693214,
       // load事件被发送时的Unix毫秒时间戳。如果这个事件还未被发送,它的值将会是0。
       loadEventStart: 1441112693214,
       // 当load事件结束,即加载事件完成时的Unix毫秒时间戳。如果这个事件还未被发送,或者尚未完成,它的值将会是0.
       loadEventEnd: 1441112693215
    }
};

通过这些属性, 能够计算出一些重要的网页性能数据

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

// ## 计算加载时间
function getPerformanceTiming () {
    const performance = window.performance;

    if (!performance) {
     // 当前浏览器不支持
     return;
    }

    const { loadEventEnd, navigationStart, domComplete, responseEnd, redirectEnd, redirectStart, domainLookupEnd, domainLookupStart, responseStart, requestStart,         loadEventStart, fetchStart, unloadEventEnd, unloadEventStart, connectEnd, connectStart } = performance.timing;
    const times = {};

    //页面加载完成的时间
    times.loadPage = loadEventEnd - navigationStart;

    //【重要】解析 DOM 树结构的时间
    times.domReady = domComplete - responseEnd;

    //  重定向的时间
    times.redirect = redirectEnd - redirectStart;

    //【重要】DNS 查询时间
    times.lookupDomain = domainLookupEnd - domainLookupStart;

    //【重要】读取页面第一个字节的时间
    //  TTFB 即 Time To First Byte
    times.ttfb = responseStart - navigationStart;

    //【重要】内容加载完成的时间
    times.request = responseEnd - requestStart;

    //【重要】执行 onload 回调函数的时间
    times.loadEvent = loadEventEnd - loadEventStart;

    // DNS 缓存时间
    times.appcache = domainLookupStart - fetchStart;

    // 卸载页面的时间
    times.unloadEvent = unloadEventEnd - unloadEventStart;

    // TCP 建立连接完成握手的时间
    times.connect = connectEnd - connectStart;

    return times;
}

C_补 2 性能指标落地

1. 指标获取方式

1
2
3
4
5
6
7
8
9
// ## (1)navigation相关: performance.getEntriesByType('navigation')[0]  ||  performance.timing
// 获取navigation-time
let _performance = window.performance || window.msPerformance || window.webkitPerformance;
let timing = _performance.getEntriesByType && _performance.getEntriesByType('navigation') && _performance.getEntriesByType('navigation')[0] || performance.timing

// ##(2)Paint相关:
// 获取首次绘制时间、首次内容绘制 || 不需要通过遍历获取 first-contentful-pain 和 first-paint
var perfEntries = _performance.getEntries();
for (var key in perfEntries) { if() {} }

2. 通过上述方式获取到的指标及其含义

(1) navigation-time
(2) paint
performance.getEntriesByType(‘paint’) // 返回数组包含 [PerformancePaintTiming, PerformancePaintTiming]
分别为:first-contentful-paint, first-paint
PerformancePaintTiming : {duration: 0, entryType: "paint", name: "first-paint", startTime: 552.2650000057183 }

3. 常用分析指标及计算方式

dns, tcp, ttfb, trans,
dom, res, ssl, firstbyte, tti, ready, load,
fp, fcp, fmp, …

新的性能指标:LCP、TBT、CLS
最大内容渲染 (Largest Contentful Paint - LCP), 页面阻塞总时长(Total Blocking Time - TBT) 和 累积布局位移 (Cumulative Layout Shift - CLS) 代表了新一代的性能数据,这些指标将更加聚焦于用户真实体验,而不仅仅是初始化加载时间例如首次内容加载时间(FCP)和首次有意义内容加载(FMP)。

knowledge is no pay,reward is kindness
0%