亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

在單頁應(yīng)用中,如何優(yōu)雅的上報(bào)前端性能數(shù)據(jù)

KitorinZero / 948人閱讀

摘要:本文的介紹的是如何設(shè)計(jì)一個(gè)通用的可以以較小的侵入性,自動(dòng)上報(bào)前端的性能數(shù)據(jù)。具體的做法可以看我的上一篇文章在單頁應(yīng)用中,如何優(yōu)雅的監(jiān)聽的變化。三如何上報(bào)性能數(shù)據(jù)那么如何上報(bào)性能數(shù)據(jù)呢,我們第一反應(yīng)就是通過請(qǐng)求的形式來上報(bào)前端性能數(shù)據(jù)。

??最近在做一個(gè)較為通用的前端性能監(jiān)控平臺(tái),區(qū)別于前端異常監(jiān)控,前端的性能監(jiān)控主要需要上報(bào)和展示的是前端的性能數(shù)據(jù),包括首頁渲染時(shí)間、每個(gè)頁面的白屏?xí)r間、每個(gè)頁面所有資源的加載時(shí)間以及每一個(gè)頁面中所以請(qǐng)求的響應(yīng)時(shí)間等等。

??本文的介紹的是如何設(shè)計(jì)一個(gè)通用的jssdk,可以以較小的侵入性,自動(dòng)上報(bào)前端的性能數(shù)據(jù)。主要采用的是Performance API以及sendBeacon方法等等。主要參考的是google analytics以及阿里云前端性能監(jiān)控平臺(tái)的實(shí)踐。

??在我的項(xiàng)目中使用nestjs作為后端框架,nestjs是基于express的一款完美支持typescript,類java spring的node后端框架。本文主要側(cè)重與如何上報(bào)性能數(shù)據(jù),后端處理邏輯比較簡單,不會(huì)具體介紹,因此不需要了解如何使用nestjs。本文的主要內(nèi)容包含了:

根據(jù)Performance API獲取前端性能數(shù)據(jù)

何時(shí)應(yīng)該上報(bào)性能數(shù)據(jù)

如何上報(bào)性能數(shù)據(jù)

原文在我的博客中,歡迎star

https://github.com/forthealll...

一、根據(jù)Performance API 獲取前端性能數(shù)據(jù)

本文上報(bào)的前端性能數(shù)據(jù)包含兩部分,一是通過Performance API獲得的性能數(shù)據(jù),二是自定義的在每個(gè)頁面應(yīng)該上報(bào)的數(shù)據(jù)。

首先來看通過Performance API所獲取的數(shù)據(jù),該數(shù)據(jù)也包含了兩個(gè)部分,當(dāng)前頁面的性能相關(guān)數(shù)據(jù)以及當(dāng)前頁面資源加載和異步請(qǐng)求的相關(guān)數(shù)據(jù)。

(1)、Performance API 所提供的性能數(shù)據(jù)

window.performance.timing會(huì)返回一個(gè)對(duì)象,該對(duì)象包含了各種與頁面渲染所相關(guān)的數(shù)據(jù)。本文不會(huì)具體去介紹該對(duì)象,只給出根據(jù)該對(duì)象計(jì)算相關(guān)性能數(shù)據(jù)的方法:

  let times = {};
  let t = window.performance.timing;
  
  //重定向時(shí)間
  times.redirectTime = t.redirectEnd - t.redirectStart;
  
  //dns查詢耗時(shí)
  times.dnsTime = t.domainLookupEnd - t.domainLookupStart;
  
  //TTFB 讀取頁面第一個(gè)字節(jié)的時(shí)間
  times.ttfbTime = t.responseStart - t.navigationStart;
  
  //DNS 緩存時(shí)間
  times.appcacheTime = t.domainLookupStart - t.fetchStart;
  
  //卸載頁面的時(shí)間
  times.unloadTime = t.unloadEventEnd - t.unloadEventStart;
  
  //tcp連接耗時(shí)
  times.tcpTime = t.connectEnd - t.connectStart;
  
  //request請(qǐng)求耗時(shí)
  times.reqTime = t.responseEnd - t.responseStart;
  
  //解析dom樹耗時(shí)
  times.analysisTime = t.domComplete - t.domInteractive;
  
  //白屏?xí)r間
  times.blankTime = t.domLoading - t.fetchStart;
  
  //domReadyTime
  times.domReadyTime = t.domContentLoadedEventEnd - t.fetchStart;

在上面的times對(duì)象中就包含了性能相關(guān)的屬性,根據(jù)performance.timing中的相關(guān)屬性計(jì)算就可以得到結(jié)果。在這里我們認(rèn)為domReadyTime就是首屏加載的時(shí)間,此外也可以自定義的方法上報(bào)首屏的時(shí)間:

比如有些場(chǎng)景可以認(rèn)為是dom增量最大的點(diǎn)為首屏渲染完成的時(shí)間,也有一些場(chǎng)景可以定義可見的dom在增量最大處為首屏渲染完成的時(shí)間。

(2)、Performance API 所提供的資源加載和請(qǐng)求數(shù)據(jù)

??可以通過window.performance.getEntries()來獲取資源的加載和請(qǐng)求相關(guān)的數(shù)據(jù)。每一個(gè)頁面中,需要去加載很多資源比如js、css等等,同時(shí)在頁面中還會(huì)存在一些異步請(qǐng)求。通過window.performance.getEntries()可以獲得這些資源加載和異步請(qǐng)求所相關(guān)的數(shù)據(jù)。我們可以通過如下的方式來獲取加載和異步請(qǐng)求的數(shù)據(jù):

  
  let  entryTimesList = [];
  let entryList = window.performance.getEntries();
  entryList.forEach((item,index)=>{
  
     let templeObj = {};
     
     let usefulType = ["navigation","script","css","fetch","xmlhttprequest","link","img"];
     if(usefulType.indexOf(item.initiatorType)>-1){
       templeObj.name = item.name;
       
       templeObj.nextHopProtocol = item.nextHopProtocol;
      
       //dns查詢耗時(shí)
       templeObj.dnsTime = item.domainLookupEnd - item.domainLookupStart;

       //tcp鏈接耗時(shí)
       templeObj.tcpTime = item.connectEnd - item.connectStart;
       
       //請(qǐng)求時(shí)間
       templeObj.reqTime = item.responseEnd - item.responseStart;

       //重定向時(shí)間
       templeObj.redirectTime = item.redirectEnd - item.redirectStart;

       entryTimesList.push(templeObj);
     }
  });

我們通過window.performance.getEntries()獲得一個(gè)帶有資源加載和異步請(qǐng)求相關(guān)數(shù)據(jù)的數(shù)組,然后根據(jù)數(shù)組中每一個(gè)元素的initiatorType屬性來過濾出屬性為["navigation","script","css","fetch","xmlhttprequest","link","img"]之一的元素?cái)?shù)據(jù)。

(3)、注意點(diǎn)

通過window.performance.timing所獲的的頁面渲染所相關(guān)的數(shù)據(jù),在單頁應(yīng)用中改變了url但不刷新頁面的情況下是不會(huì)更新的。因此如果僅僅通過該api是無法獲得每一個(gè)子路由所對(duì)應(yīng)的頁面渲染的時(shí)間。如果需要上報(bào)切換路由情況下每一個(gè)子頁面重新render的時(shí)間,需要自定義上報(bào)。

通過window.performance.getEntries()所獲取的資源加載和異步請(qǐng)求所相關(guān)的數(shù)據(jù),在頁面切換路由的時(shí)候會(huì)重新的計(jì)算,可以實(shí)現(xiàn)自動(dòng)的上報(bào)。

二、何時(shí)上報(bào)性能數(shù)據(jù)

??接著來確定應(yīng)該何時(shí)上報(bào)性能數(shù)據(jù),因?yàn)橐幚韕v(訪問量)和uv(獨(dú)立用戶訪問量),一般認(rèn)為一次上報(bào)就是一次訪問,那么何時(shí)上報(bào)性能數(shù)據(jù)呢。在我的系統(tǒng)中選擇在一下場(chǎng)景下進(jìn)行一次前端性能數(shù)據(jù)的上報(bào):

頁面加載和重新刷新

頁面切換路由

頁面所在的tab標(biāo)簽重新變得可見

針對(duì)上述的3種場(chǎng)景,特別是切換路由的情況,如果切換路由是通過改變hash值來實(shí)現(xiàn)的,那么只需要監(jiān)聽hashchange事件,如果是通過html5的history api來改變url的,那么需要重新定義pushstate和replacestate事件。具體的做法可以看我的上一篇文章:在單頁應(yīng)用中,如何優(yōu)雅的監(jiān)聽url的變化。

直接給出history實(shí)現(xiàn)路由場(chǎng)景下監(jiān)聽url改變的方案:

var _wr = function(type) {
   var orig = history[type];
   return function() {
       var rv = orig.apply(this, arguments);
      var e = new Event(type);
       e.arguments = arguments;
       window.dispatchEvent(e);
       return rv;
   };
};
 history.pushState = _wr("pushState");
 history.replaceState = _wr("replaceState");
 

然后我們就可以根據(jù)上述場(chǎng)景,分別監(jiān)聽相應(yīng)的事件,從而實(shí)現(xiàn)前端性能數(shù)據(jù)的上報(bào):

addEvent(window,"load",function(e){
    ...deal with something
});
//監(jiān)控history基礎(chǔ)上實(shí)現(xiàn)的單頁路由中url的變化
addEvent(window,"replaceState", function(e) {
    ...deal with something
});
addEvent(window,"pushState", function(e) {
    ...deal with something
});
//通過hash切換來實(shí)現(xiàn)路由的場(chǎng)景
addEvent(window,"hashchange",function(e){
   ...deal with something
});
addEvent("document","visibilitychang",function(e){
   ...deal with something
})

addEvent是一個(gè)兼容IE和標(biāo)準(zhǔn)DOM事件流模型的事件。

三、如何上報(bào)性能數(shù)據(jù)

??那么如何上報(bào)性能數(shù)據(jù)呢,我們第一反應(yīng)就是通過ajax請(qǐng)求的形式來上報(bào)前端性能數(shù)據(jù)。這種方法有一些缺陷,比如必須對(duì)跨域做特殊處理以及如果頁面銷毀后,相應(yīng)的ajax方法并不一定發(fā)送成功等問題。

其中跨域的問題比較好處理,最難解決的問題是第二點(diǎn):

就是如果頁面銷毀,那么對(duì)應(yīng)的ajax方法并不一定能成功發(fā)送。

??我們可以根據(jù)google analytics(GA)中的方法,根據(jù)瀏覽器的兼容性以及url的長度,來采用不同的方法上報(bào)性能數(shù)據(jù),主要原理是:

通過動(dòng)態(tài)創(chuàng)建img標(biāo)簽的方式,在img.src中拼接url的方式發(fā)送請(qǐng)求,不存在跨域限制。如果url太長,則才用sendBeacon的方式發(fā)送請(qǐng)求,如果sendBeacon方法不兼容,則發(fā)送ajax post同步請(qǐng)求

(1)、sendBeacon方法

??解決在文檔卸載或者頁面關(guān)閉后無法完成異步ajax請(qǐng)求的問題,很多情況下我們會(huì)把異步變成同步。在頁面卸載的unload或者beforeunload事件中執(zhí)行同步方法調(diào)用。

但是同步方法調(diào)用存在一個(gè)問題,就是會(huì)推遲A頁面切換進(jìn)入B頁面的時(shí)間。而sendBeacon方法解決了該問題,簡單來說:

sendBeacon方法在頁面銷毀期,可以異步的發(fā)送數(shù)據(jù),因此不會(huì)造成類似同步ajax請(qǐng)求那樣的阻塞問題,也不會(huì)影響下一個(gè)頁面的渲染

sendBeacon的調(diào)用方式為:

navigator.sendBeacon(url [, data]);

data可以為: ArrayBufferView, Blob, DOMString, 或者 FormData

為了發(fā)送參數(shù),我們一般data制定為Blob的形式。此外還要注意的是,在sendBeacon的請(qǐng)求頭header中,不支持Content-Type為“application/json; charset=utf-8”。

在sendBeacon的header中,只支持一下3種形式的Content—Type:

application/x-www-form-urlencoded

multipart/form-data

text/plain

一般制定為application/x-www-form-urlencoded,完整的通過sendBeacon來發(fā)送請(qǐng)求的例子如下:

function sendBeacon(url,data){
  //判斷支不支持navigator.sendBeacon
  let headers = {
    type: "application/x-www-form-urlencoded"
  };
  let blob = new Blob([JSON.stringify(data)], headers);
  navigator.sendBeacon(url,blob);
}

后端如何處理sendBeacon請(qǐng)求呢,sendBeacon在的請(qǐng)求頭中發(fā)送的是一個(gè)類似與POST的請(qǐng)求,因此可以類似于處理post一樣來處理sendBeacon請(qǐng)求。

一般我們約定ajax請(qǐng)求的content—type為:“application/json; charset=utf-8”,而sendBeacon請(qǐng)求的content-type為:“application/x-www-form-urlencoded”,這樣在后端處理中,就可以區(qū)別是正常的ajax post請(qǐng)求還是sendBeacon請(qǐng)求。

此外,在處理請(qǐng)求的時(shí)候如果存在跨域問題,通過cors跨域的方式來處理,后端需要配置:allow-control-allow-origin等,可以通過express的cors包,來簡化配置:

async function bootstrap() {
  const app = await NestFactory.create(ApplicationModule,instance);
  app.use(cors());

  await app.listen(3000)
}
bootstrap();


(2)動(dòng)態(tài)創(chuàng)建img標(biāo)簽的形式

??通過動(dòng)態(tài)創(chuàng)建img標(biāo)簽的形式,指定src屬性所指定的url來發(fā)送請(qǐng)求,首先不受跨域的限制,其次img標(biāo)簽動(dòng)態(tài)插入,會(huì)延遲頁面的卸載保證圖片的插入,因此可以保證在頁面的銷毀期,請(qǐng)求可以發(fā)生。

下面是一個(gè)動(dòng)態(tài)創(chuàng)建img標(biāo)簽的例子:

function imgReport(url, data) {
   if (!url || !data) {
       return;
   }
   let image = document.createElement("img");
   let items = [];
   items = JSON.Parse(data);
   let name = "img_" + (+new Date());
   image.onload = image.onerror = function () {
      
   };
   let newUrl = url + (url.indexOf("?") < 0 ? "?" : "&") + items.join("&");

   image.src = newUrl;
}

此外,我們?cè)趧?dòng)態(tài)創(chuàng)建img標(biāo)簽發(fā)送請(qǐng)求的時(shí)候,請(qǐng)求的是一張圖片,在后端處理的時(shí)候,要在末尾將這個(gè)圖片返回,這樣前端的image.onload方法才會(huì)被觸發(fā)。我們以請(qǐng)求的地址為:localhost:8080/1.jpg為例,后端的處理邏輯為:

@Controller("1.jpg")
export class AppUploadController {
  constructor(private readonly appService: AppService) {}
  @Get()
  getUpload(@Req() req,@Res() res): void {
  
    ...deal with some thing
    res.sendFile(join(__dirname, "..", "public/1.jpg"))
  }
}

在get請(qǐng)求的處理中,我們通過res.sendFile(join(__dirname, "..", "public/1.jpg"))將圖片返回后,這樣前端的image的onload方法才會(huì)被調(diào)用。

(3)同步ajax post請(qǐng)求

??動(dòng)態(tài)創(chuàng)建img標(biāo)簽的方法,拼接url的時(shí)候存在一定的問題,因?yàn)闉g覽器對(duì)url的長度是有限制的。而sendBeacon方法兼容性不是很好,最后兜底的處理方式就是發(fā)送同步的ajax請(qǐng)求,同步的ajax請(qǐng)求前面說過,會(huì)在頁面銷毀期之前執(zhí)行,雖然會(huì)有一定程度的阻塞下一個(gè)頁面的渲染。

function xmlLoadData(url,data) {
  var client = new XMLHttpRequest();
  client.open("POST", url,false);
  client.setRequestHeader("Content-Type", "application/json; charset=utf-8");
  client.send(JSON.stringify(data));
}

(4)綜合解決方案

??一般首先拼接攜帶參數(shù)的完整的url,判斷url的長度,如果url的長度小于瀏覽器允許的最大長度內(nèi),那么通過動(dòng)態(tài)創(chuàng)建img標(biāo)簽的形式來發(fā)送前端性能數(shù)據(jù),如果url太長,則判斷瀏覽器是否支持sendBeacon方法,如果支持,則通過sendBeacon方法來發(fā)送請(qǐng)求,否則發(fā)送同步的ajax請(qǐng)求。

function dealWithUrl(url,appId){
      let times = performanceInfo(appId);
      let items = decoupling(times);
      let urlLength = (url + (url.indexOf("?") < 0 ? "?" : "&") + items.join("&")).length;
      if(urlLength<2083){
        imgReport(url,times);
      }else if(navigator.sendBeacon){
        sendBeacon(url,times);
      }else{
        xmlLoadData(url,times);
      }
    }

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/117254.html

相關(guān)文章

  • 單頁應(yīng)用,如何優(yōu)雅上報(bào)前端性能數(shù)據(jù)

    摘要:本文的介紹的是如何設(shè)計(jì)一個(gè)通用的可以以較小的侵入性,自動(dòng)上報(bào)前端的性能數(shù)據(jù)。具體的做法可以看我的上一篇文章在單頁應(yīng)用中,如何優(yōu)雅的監(jiān)聽的變化。三如何上報(bào)性能數(shù)據(jù)那么如何上報(bào)性能數(shù)據(jù)呢,我們第一反應(yīng)就是通過請(qǐng)求的形式來上報(bào)前端性能數(shù)據(jù)。 ??最近在做一個(gè)較為通用的前端性能監(jiān)控平臺(tái),區(qū)別于前端異常監(jiān)控,前端的性能監(jiān)控主要需要上報(bào)和展示的是前端的性能數(shù)據(jù),包括首頁渲染時(shí)間、每個(gè)頁面的白屏?xí)r...

    curried 評(píng)論0 收藏0
  • 單頁應(yīng)用如何優(yōu)雅上報(bào)前端性能數(shù)據(jù)

    摘要:本文的介紹的是如何設(shè)計(jì)一個(gè)通用的可以以較小的侵入性,自動(dòng)上報(bào)前端的性能數(shù)據(jù)。具體的做法可以看我的上一篇文章在單頁應(yīng)用中,如何優(yōu)雅的監(jiān)聽的變化。三如何上報(bào)性能數(shù)據(jù)那么如何上報(bào)性能數(shù)據(jù)呢,我們第一反應(yīng)就是通過請(qǐng)求的形式來上報(bào)前端性能數(shù)據(jù)。 ??最近在做一個(gè)較為通用的前端性能監(jiān)控平臺(tái),區(qū)別于前端異常監(jiān)控,前端的性能監(jiān)控主要需要上報(bào)和展示的是前端的性能數(shù)據(jù),包括首頁渲染時(shí)間、每個(gè)頁面的白屏?xí)r...

    beanlam 評(píng)論0 收藏0
  • 單頁應(yīng)用如何優(yōu)雅監(jiān)聽url變化

    摘要:單頁應(yīng)用的原理從早起的根據(jù)的變化,到根據(jù)的的變化,實(shí)現(xiàn)無刷新條件下的頁面重新渲染。那么在單頁應(yīng)用中是如何監(jiān)聽的變化呢,本文將總結(jié)一下,如何在單頁頁面中優(yōu)雅的監(jiān)聽的變化。在下幾章中,重點(diǎn)介紹一下如何監(jiān)聽的改變。 ??單頁應(yīng)用的原理從早起的根據(jù)url的hash變化,到根據(jù)H5的history的變化,實(shí)現(xiàn)無刷新條件下的頁面重新渲染。那么在單頁應(yīng)用中是如何監(jiān)聽url的變化呢,本文將總結(jié)一下,...

    leap_frog 評(píng)論0 收藏0
  • 單頁應(yīng)用,如何優(yōu)雅監(jiān)聽url變化

    摘要:單頁應(yīng)用的原理從早起的根據(jù)的變化,到根據(jù)的的變化,實(shí)現(xiàn)無刷新條件下的頁面重新渲染。那么在單頁應(yīng)用中是如何監(jiān)聽的變化呢,本文將總結(jié)一下,如何在單頁頁面中優(yōu)雅的監(jiān)聽的變化。在下幾章中,重點(diǎn)介紹一下如何監(jiān)聽的改變。 ??單頁應(yīng)用的原理從早起的根據(jù)url的hash變化,到根據(jù)H5的history的變化,實(shí)現(xiàn)無刷新條件下的頁面重新渲染。那么在單頁應(yīng)用中是如何監(jiān)聽url的變化呢,本文將總結(jié)一下,...

    姘存按 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<