深圳幻海软件技术有限公司 欢迎您!

记录--前端性能监控初步实战

2023-03-03

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助前言在当下前后端分离的主流环境下,前端部分的优化变得越来越重要。为了提升前端的性能和用户体验,我觉得可能需要从三个维度采集数据进行分析。前端埋点。通过埋点收集和统计网页的UV/PV、设备型号、浏览器等数据进行分析,比如可以有针对性对使用比较

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

前言

在当下前后端分离的主流环境下,前端部分的优化变得越来越重要。为了提升前端的性能和用户体验,我觉得可能需要从三个维度采集数据进行分析。

  1. 前端埋点。通过埋点收集和统计网页的UV/PV、设备型号、浏览器等数据进行分析,比如可以有针对性对使用比较靠前的设备、浏览器等做优化和体验。
  2. 网页性能收集和监控。 采集一个页面从请求开始到完成这个过程中的数据指标。比如收集和监控首屏加载时间、dom渲染时长、响应比较慢的接口等。有了这些数据可以很直观和针对性的对网页的性能进行优化和升级。
  3. 错误收集和监控。收集网页中的js的报错、静态资源加载报错,保证网页正常访问和降低bug率。

1.前端埋点

采集用户的行为,监控产品在用户端的使用情况,根据数据可以明确,前端可以针对性的做优化和体验。

可以简单的初步建立这样的数据模型:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
 ip: ip地址,统计uv
 ua: 浏览器的userAgent //方便区分浏览器的品牌
 os: 系统名称
 current_page_all: 当前页面访问总次数
 width:浏览器宽度,  
 height:浏览器高度, //统计浏览器的尺寸
 current_enter_time: 进入当前页面的时间戳
 current_leave_time: 离开当前页面的时间戳
 project_id: 项目的id,
 url: 当前url,
 user_uni_id: 临时分配给用户唯一的id
 ....
 还有其他
}

2.在网页中植入对应的js代码

?
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
//进入
document.addEventListener('DOMContentLoaded',function(){
   ...
   let args = {
     ua: navigator.userAgent,
     os: navigator.platform,
     width: document.body.clientWidth || document.documentElement.clientWidth,
     height: document.body.clientHeight || document.documentElement.clientHeight,
     project_id: md5('abcd'),
     user_uni_id: '临时分配给用户唯一的id',
     url: window.location.href,
     current_enter_time: new Date().getTime()
     ....
   };
   let img = new Image();
   img.onload = function() {
     img = null;
   };
   img.src= `https://localhost:9700/bury.gif?args=${qs(args)}`;
 });
 
//离开
window.onbeforeunload = function() {
   ...
   let args = {
     ...
     leave_time: new Date().getTime()
     ....
   };
   let img = new Image();
   img.onload = function() {
     img = null;
   };
   img.src= `https://localhost:9700/bury.gif?args=${qs(args)}`;
}
  1. 收集数据并上报
?
1
2
3
4
5
6
7
8
9
10
11
12
const fs = require('fs');
route.get('/bury.gif',async (ctx)=>{
  let queryStr = ctx.querystring;
  let d = new Date();
  let year = d.getFullYear();
  let month = d.getMonth()+1;
  let day = d.getDate()+1;
  fs.writeFile(`../logs/${year}-${month}-${day}.log`,queryStr,{flag:'a',encoding:'utf-8',mode:'0666'},function(e){});
   ctx.status = 200;
   ctx.type = 'image/gif';
   ctx.body = {};
});

简单的设想方案是先把数据收集到文本文件里,然后定时的分析这些文本文件,然后把筛选后的放到数据库中。

2.网页性能收集

网页性能主要是收集是输入url地址到网页请求完成资源下载完成这段时间范围内一些请求、加载等指标。

  • 比如举几个简单的指标:

  • 首屏加载时长

  • HTML 文档被加载和解析完成

  • 网页加载完成时间

  • 白屏时间

  • 其他…

  1. 具体实现方案(一):
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//index.html
<html>
 <head>
   <script>
     window.startTime = Date.now();
   </script>
 </head>
 <body>
   <script>
     let diff = Date.now()-window.startTime;
     console.log('白屏时长'+diff);
     document.addEventListener('DOMContentLoaded',()=>{
       console.log('HTML 文档被加载和解析完成时间'+Date.now()-window.startTime);
     });
     window.onload = function() {
       console.log('网页加载完成时间'+Date.now()-window.startTime);
     };
   </script>
   <script src="a.js"></script>
   <script src="b.js"></script>
   ....
 </body>
</html>

使用performance API

Performance是W3C性能小组引入进来的一个新的API,他可以很好的获取到首屏加载时间、白屏时间、dns查询时间等,是一个很方便的获取网页性能指标的API,而且目前大部分主流浏览器是支持的。

?
1
https://www.caniuse.com/

1.Performance一些常用用法的总结

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let timing = window.performance.timing
//白屏时间
timing.responseStart - timing.navigationStart
//DNS 查询时长
timing.domainLookupEnd - timing.domainLookupStart
//request请求耗时
timing.responseEnd - timing.responseStart
//HTML 文档被加载和解析完成耗时
timing.domComplete - timing.domInteractive
//网页加载完成耗时
timing.loadEventEnd - timing.navigationStart
//重定向耗时
timing.redirectEnd - timing.redirectStart;
//占用的内存
window.performance.memory.usedJSHeapSize;

2.此外还有一些高级用法,比如可以收集一些请求和静态资源的请求时间

?
1
2
3
4
5
6
7
8
9
10
11
12
13
let  time = [];
let entryLists = window.performance.getEntries();
for(let i=0;i<entryLists.length;i++) {
  let item = entryLists[i];
  let obj = {};
     let soureTypes = ['script','css','xmlhttprequest','link','img'];
     if(soureTypes.indexOf(item.initiatorType)>=0){
       obj.name = item.name; 
       //请求时间
       obj.reqTime = item.responseEnd - item.responseStart;
       time.push(obj);
     }
}

3.关于Performance的更多用法可以参考:

?
1
2
https://www.jianshu.com/p/1355232d525a
https://blog.csdn.net/hb_zhouyj/article/details/89888646

4.收集数据上报

收集上报数据,使用koa2创建接口performance.gif

?
1
2
3
4
5
6
7
8
9
10
11
12
const fs = require('fs');
route.get('/performance.gif',async (ctx)=>{
  let queryStr = ctx.querystring;
  let d = new Date();
  let year = d.getFullYear();
  let month = d.getMonth()+1;
  let day = d.getDate()+1;
  fs.writeFile(`../performance-logs/${year}-${month}-${day}.log`,queryStr,{flag:'a',encoding:'utf-8',mode:'0666'},function(e){});
   ctx.status = 200;
   ctx.type = 'image/gif';
   ctx.body = {};
});

3.错误收集和监控

错误收集主要就是针对js报错、静态资源加载等出错信息进行收集。

  1. 收集js报错最先想到的可能是try catch。
?
1
2
3
4
5
6
try {
    console.log(b);
    } catch(e) {
      console.log(e);
      sendErrorReq();
    };

但是使用使用的话,每个页面收集错误都要充斥这try catch,这样其实是不太好的。

而且catch似乎没办法捕获到异步的操作。

?
1
2
3
4
5
6
7
8
try {
      setTimeout(()=>{
       console.log(b);
      })
    } catch(e) {
      console.log(e);
      sendErrorReq();
    };

试了一下没有执行到catch里边的sendErrorReq函数。

使用try catch是一种局部错误监听方式。

  1. 用window.onerror或者是window.addEventListener(‘error’)

window.onerror

?
1
2
3
4
5
6
7
8
9
10
/**
      *msg 错误信息
      *url 错误所在的页面地址
      *row 错误所在的行数
      *col 错误所在的列数
      **/
     window.onerror = function(msg,url,row,col) {
       console.log(msg,url,row,col, error);
     };
    b();

使用window.onerror可以检测到js的报错,但是没法监听静态资源加载失败的情况。

?
1
<img src="一个不存在的图片地址" alt="">

window.addEventListener(‘error’)

window.addEventListener(‘error’)能够监听到静态资源加载出错

?
1
2
3
4
5
6
7
8
9
10
window.addEventListener('error',(e)=>{
      let localName = e.srcElement.localName;
      let currentSrc = e.srcElement.currentSrc;
      if(localName=='img') {
        console.log(`图片${currentSrc}加载失败了`);
        sendErrorData(currentSrc);
      }
      ...
      // e.preventDefault();
    },true);

必须设为捕获过程中执行,否则依然无法监听。

3. promise错误的收集

?
1
2
3
4
5
6
7
8
9
10
new Promise((resolve,reject)=>{
       reject('hi');
    }).catch(e=>{
      console.log(e);
      sendErrorData(e)
    });
axios.get(...).catch(e=>{
  console.log(e);
   sendErrorData(e)
})

但是有种情况,如果promise不加catch的话,
没法通过window.onerror去监听,但是还是通过监听unhandledrejection事件去收集的

4.unhandledrejection

?
1
2
3
4
5
6
7
window.addEventListener('unhandledrejection', event => {
       console.log('error:'+event.reason);
        sendErrorData(event.reason);
    });  
    new Promise((resolve,reject)=>{
       reject('hi');
    });

在看下在vue中收集报错,以vue-cli3创建的项目进行演示

测试了一下window.onerror这种方式 无法监听错误的。

在网上找了下原因

可以看到在vue的源码里,因为如果没有定义errorHandler就会走到logError这个方法,所以没法使用window.onerror进行监听。

5.errorHandler

在vue的手册中,推荐监听vue报错的可以使用errorHandler这个配置方法。

?
1
2
3
4
5
6
7
8
9
//main.js
Vue.config.errorHandler = function (err, vm, info) {
  console.log('错误是:', err)
  sendErrorData(err);
}
然后随便故意写错
mounted() {
  a();
}
 
就能收集到报错信息了

 当然最后把收集的错误上报给服务器,创建一个接口error.gif。

?
1
2
3
4
5
6
7
8
9
const fs = require('fs');
route.get('/error.gif',async (ctx)=>{
  let queryStr = ctx.querystring;
  ....
  fs.writeFile(`../error-logs/${year}-${month}-${day}.log`,queryStr,{flag:'a',encoding:'utf-8',mode:'0666'},function(e){});
   ctx.status = 200;
   ctx.type = 'image/gif';
   ...
});

本文转载于:

http://events.jianshu.io/p/a6572eb10e00

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

 


__EOF__

  • 本文作者: 林恒
  • 本文链接: https://www.cnblogs.com/smileZAZ/p/17044397.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。