这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
前言
在当下前后端分离的主流环境下,前端部分的优化变得越来越重要。为了提升前端的性能和用户体验,我觉得可能需要从三个维度采集数据进行分析。
- 前端埋点。通过埋点收集和统计网页的UV/PV、设备型号、浏览器等数据进行分析,比如可以有针对性对使用比较靠前的设备、浏览器等做优化和体验。
- 网页性能收集和监控。 采集一个页面从请求开始到完成这个过程中的数据指标。比如收集和监控首屏加载时间、dom渲染时长、响应比较慢的接口等。有了这些数据可以很直观和针对性的对网页的性能进行优化和升级。
- 错误收集和监控。收集网页中的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 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 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报错、静态资源加载等出错信息进行收集。
- 收集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是一种局部错误监听方式。
- 用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__