作者:李飞飞,京东零售1加载状态如何,接口请求需要多长时间,什么时候挂,为什么挂,这些都不清楚。同时,在产品推广的过程中,往往需要统计页面的使用情况和用户行为,这样才能从运营和产品的角度了解用户群体,进而迭代升级产品到使其更贴近用户,为业务拓展提供更多服务。可能性。因此,我们需要一个前端页面监控系统,对页面性能状态进行持续监控和预警,发现瓶颈时用于指导优化。2前端监控目标前端监控主要包括两部分:性能监控和异常监控,保证稳定性(异常监控)错误监控包括JavaScript代码错误、Promsie错误、接口(XHR、fetch)错误、资源加载错误(script,link,etc.)等,这些错误大多会导致页面功能异常甚至白屏。提升用户体验(性能监控)性能监控包括页面加载时间、界面响应时间等,反映用户体验的好坏。3性能监控3.1页面加载简述简单看一下,从输入url到页面加载完成的过程如下:首先需要通过DNS(DomainNameResolutionSystem)将URL解析到对应的IP地址),然后就是用这个IP地址确定的服务器建立TCP网络连接,然后我们向服务器抛出一个HTTP请求。服务器处理完我们的请求后,将目标数据放入HTTP响应中返回给客户端。得到响应数据的浏览器就可以开始走一个渲染流程了。渲染后,页面呈现给用户。我们可以将这个过程分为以下几个流程片段:DNS解析TCP连接HTTP请求抛出服务器处理请求,HTTP响应返回给浏览器获取响应数据,解析响应内容,并将解析结果显示给user3.2从开发者的角度来看,在页面加载的每个阶段,查看从输入url到用户可以使用页面的整个过程的时间统计,会返回一个PerformanceTiming对象,单位为毫秒。关于性能,在《从前端角度浅谈性能》中已经介绍过了,这里再强调一下:每个阶段的性能和能耗可以通过API:window.performance获取,对应的具体方法有:performance.timing,performance.getEntriesByType('resource'),performance.navigation等。如上,开发者可以通过performance中各阶段的时间戳,各静态资源的具体加载时间,页面是否被加载,来获取页面各阶段的性能指标重定向和重定向时间。将所有属性按触发顺序排列:navigationStart:在同一个浏览器上下文中,上一个网页卸载的时间戳(不一定要和当前页面同一个域),如果没有上一个网页卸载,则相等到fetchStart的值redirectStart:first第一次HTTP重定向发生的时间。如果有跳转,并且是同域名内的重定向,否则值为0unloadEventStart:上一个网页卸载的时间戳(与当前页面同域),如果没有上一个网页卸载或者域上一个网页的和当前页面不一样,则值为0redirectEnd:最后一次HTTP重定向完成的时间。仅当有跳转且为同域名内重定向,否则值为0unloadEventEnd:对应unloadEventStart,返回上一个网页unload事件绑定的回调函数执行时的时间戳。fetchStart:浏览器准备好使用HTTP请求来获取文档的时间,发生在检查本地缓存之前。domainLookupStart:DNS域名查询开始的时间。如果使用本地缓存(即没有DNS查询)或持久连接,则它等于fetchStart值。domainLookupEnd:DNS域名查询完成的时间,如果使用本地缓存(即没有DNS查询)或者长连接,则等于fetchStart的值connectStart:HTTP(TCP)开始的时间建立连接。如果是持久连接,则等于fetchStart的值。如果传输层出现错误并重新建立连接,这里显示的时间为新建立连接的开始时间secureConnectionStart:HTTPS连接开始时间,如果不是安全连接,则值为0connectEnd:HTTP(TCP)完成连接建立时间(完成握手),如果是持久连接,则等于fetchStart值。如果传输层出现错误,重新建立连接,这里会显示新建立的连接完成的时间,包括从本地读取缓存,当连接出错重连时,这里也显示新建立连接的时间localresponseEnd:HTTP收到所有response(获取到最后一个字节)的时间,包括从本地domLoading读取缓存:开始解析和渲染DOM树的时间。此时Document.readyState变为loading,会抛出一个readystatechange相关事件domInteractive:Complete当解析DOM树时,Document.readyState变为可交互的,会抛出一个readystatechange相关事件。domContentLoadedEventStart:DOM解析完成后,网页开始加载资源的时间,文档domC中DOMContentLoaded事件发生的时间ontentLoadedEventEnd:DOM解析完成后,网页中资源加载完成(如JS脚本加载执行完成)的时间,文档DOMContentLoaded事件结束时间DOM树解析完成,资源准备就绪,Document。完成,向文档发送readystatechange相关事件loadEventStart:load事件,即load回调函数开始执行的时间。如果没有绑定load事件,则值为0loadEventEnd:load事件回调函数执行完毕的时间,如果没有绑定load事件,则值为03.3各阶段性能的计算(自定义)1.`const{timing,navigation}=window.performance`1。`constloadPageInfo={};`1.``1。`//页面加载类型,区分首次加载和重新加载,0为初始加载,1为重新加载`1.`loadPageInfo.loadType=navigation.type;`1.``1。用户等待页面白屏的时间`1.`loadPageInfo.loadPage=timing.loadEventEnd-timing.navigationStart;`1。``1。`//重定向时间`1.`loadPageInfo.redirect=timing.redirectEnd-timing.redirectStart;`1.``1。`//是时候卸载页面了`1.`loadPageInfo.unloadEvent=timing.unloadEventEnd-timing.unloadEventStart;`1。``1。`//查询DNS本地缓存的时间`1.`loadPageInfo.appCache=timing.domainLookupStart-timing.fetchStart;`1.``1。`//【重要】DNS查询时间`1.`//页面是否使用过多域名不同,导致域名查询过长?推荐DNS预加载`1。`//HTML5Prefetch可用于预查询DNS`1.`loadPageInfo.lookupDomain=timing.domainLookupEnd-timing.domainLookupStart;`1。``1。`//HTTP(TCP)建立连接并完成握手的时间`1.`loadPageInfo.connect=timing.connectEnd-timing.connectStart;`1.``1。`//【重要】获取文档内容的HTTP请求和时间`1.`loadPageInfo.request=timing.responseEnd-计时。响应开始;`1。``1。`//[重要]从卸载上一个页面到HTTP获取页面的第一个字节的时间`1.`//【原因】这个可以理解为用户获取你的资源所占用的时间,建议增加远程机房,增加CDN处理,增加宽带,提高CPU运行速度`1.`//TTFB是第一个字节的时间`1。`loadPageInfo.ttfb=timing.responseStart-timing.navigationStart;`1。``1。`//解析DOM树结构的时间`1.`loadPageInfo.domReady=timing.domComplete-timing.responseEnd;`1.``1。`//【重要】执行onload回调函数的时间`1.`//【原因】onload回调函数中是否进行了太多不必要的操作,建议采用延迟加载和按需加载的策略`1.`loadPageInfo.loadEvent=timing.loadEventEnd-timing.loadE`4异常监控前端需要监控的错误主要有两类:Javascript错误(js错误、promise错误)监控错误错误(资源加载错误)4.1console.error1。`//重写console.error以捕获更全面的错误报告INFO`1。`varoldError=console.e错误;`1。``1。``1。`console.error=function(tempErrorMsg){`1.`varerrorMsg=(arguments[0]&&arguments[0].message)||tempErrorMsg;`1。`varlineNumber=0;`1。`varcolumnNumber=0;`1。`varerrorStack=arguments[0]&&arguments[0].stack;`1.``1。``1。`如果(!errorStack){`1。`saveJSError('console_error',errorMsg,'',lineNumber,columnNumber,'CustomizeError:'+errorMsg);`1.`}其他{`1.`saveJSError('console_error',errorMsg,'',lineNumber,columnNumber,errorStack);`1.`}`1。``1。``1。`returnoldError.apply(console,arguments);`4.2错误事件通过监听错误事件,js可以捕获语法,根据event.target.src/href判断资源加载错误。1.`window.addEventListener('error',function(e){`1.`varerrorMsg=e.error&&e.error.message,`1.`errorStack=e.error&&e.error.stack,`1.`pageUrl=e.filename,`1.`lineNumber=e.lineno,`1.`columnNumber=e.colno;`1.``1.``1.`saveJSError('on_error',errorMsg,pageUrl,lineNumber,columnNumber,errorStack);`1.`});`4.3Promise`//捕获未处理的Promise错误``window.onunhandledrejection=function(e){``varerrorMsg='';``varerrorStack='';``if(typeofe.reason==='object'){``errorMsg=e.reason.message;``errorStack=e.reason.stack;``}else{``errorMsg=e.reason;``errorStack='';``}``saveJSError('on_error',errorMsg,'',0,0,'UncaughtInPromiseError:'+errorStack);``}`5综上所述,通过简单的js代码可以实现页面性能和异常的监控和数据上报。后续需要相应的具体平台汇总和相应业务所需数据(如PV、UV等)的计算,才能真正实现产品的页面数据。用于业务扩展和宣传的演示文稿。6上述代码后续实现了对页面性能和异常的监控,但其实前端监控还包括对请求接口的监控和埋点的实现,会陆续推出,所以留调整。
