成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

轉轉B端項目頁面性能統計實踐

開發 項目管理
在B端項目中,頁面性能統計是非常有必要的,因為可以幫助我們了解實際用戶的具體頁面的加載速度、用戶體驗,以便了解當前頁面的質量,并且為優化頁面性能提供方向,從而提高用戶滿意度。

背景

由于轉轉前端業務方向主要偏向于 C 端,比如 App端內 H5、 小程序內 H5 等,并且技術棧以 Hybrid 為主(承載容器為轉轉標準化webview)。但是,近些年隨著業務不斷擴大,逐漸出現了如乾數據平臺、行星平臺等 專門服務 B 端的FE項目。但是沒有相關性能數據來作為參考支撐,比如需要分析用戶體驗質量;分析現有頁面性能缺陷以及后續需要做性能優化的方向等。因此,需要一款符合轉轉內部埋點上報體系的 PC 端項目網頁的性能統計平臺。

B 端性能統計面臨的問題

由于內部性能埋點統計體系不支持分批/分段上報,每個 Router 都需要作為一個單獨的頁面進行一次性的性能數據上報。在 B 端,一些新的指標需要支持和特殊處理。因此,在數據采集統計方面,我們會遇到以下幾個問題。

  1. SPA Router 問題 轉轉內部 C 端項目主要采用 hybrid 技術棧,因此不需要對 SPA 項目路由做特殊處理(因為每次都開啟一個 webview,類似于多頁面應用應用場景)。但是,基于 React 技術棧的 B 端項目是 SPA 項目,為了方便統計每個 Router 頁面的性能數據,我們需要對每個 Router 頁面的加載進行一些特殊處理。
  2. SPA 資源統計問題 現在的前端 SPA 項目一般都會通過異步加載頁面資源的方式,進行頁面打包體積的優化,以提升頁面首屏性能。因此,在進行資源統計時,我們需要單獨對相應的 Router 頁面的加載資源進行統計處理。
  3. B 端指標定義問題 轉轉 B 端性能統計主要參考核心指標:白屏、首屏、完全加載。頁面性能分數評估也主要基于這三個指標進行加權計算。但是,在 Router 頁面加載時,我們會遇到核心性能指標無法直接獲取的問題,因為 Router 切換并不會產生頁面的 load,而只是 div 的顯示隱藏。當然了,還需要其他 B 端特有的業務標識定義,這里不一一列舉。

主要內容

1. 性能指標定義

定義好哪些性能指標需要上報,是做好一個完善的采集性能數據采集 sdk 的前提條件,經過分析主要將指標分為兩類:1. 純 H5 頁面性能指標 2. 頁面相關業務性指標。

  • 純 H5 頁面性能指標
  1. 性能核心指標主要包括:白屏時間 、 首屏時間、 頁面完全加載時間,以及新增的用戶體驗指標 LCP、 FID、 CLS 。
  2. 輔助性性能指標包括:DNS 解析 、請求響應時間、 DOM開始構建時間、 頁面可交互時間、 DOM構建完成時間、 網絡速度、 各類靜態資源耗時、 ajax請求耗時、 LongTask 等等。

以上提到的絕大部分指標,可以通過瀏覽器提供的 PerformanceNavigationTiming PerformanceResourceTiming API 和 谷歌團隊提供的 web-vitals 工具函數很方便的進行獲取和計算。

  • 業務性相關指標:

所謂業務性指標,主要是作為查詢分析的一些要素,比如 我們想查詢某個業務線的某個項目的某個頁面在某個平臺下某個性能指標的表現如何?那么就需要一些非頁面性能本身的業務要素指標進行定義和上報統計。

業務指標主要包括:actiontype 埋點類型標識 、 pagetype 業務線/項目標識、pageid 頁面標識 、 clientType 端信息、 pagestate 頁面狀態、pageurl 頁面url、 cookieid 用戶id、 fromType 來源、 loadcnt 加載次數 等等。

PS: web-vitals 由于在蘋果和低版本安卓的兼容性存在問題,因此沒有在 C 端作為一個必選項,但 B 端用戶絕大多數使用 chromium 內核瀏覽器,所以大膽的將 web-vitals 納入采集指標中

2. 指標數據的獲取與上報

上面進行了各種指標的定義,那么如何高效有序的接入到轉轉埋點體系內進行上報統計呢?轉轉內部其實已經有了 C 端埋點體系,其實只需要按照一定的規則進行接入即可,主要是性能平臺B端項目需要的字段和后端已有日志表結構做好關系映射和擴展。

為了解決上面提到B端項目的特有問題,以及滿足上述提到所有性能指標、業務指標都可以很優雅的進行上報統計,方便在代碼層面更好的進行結構上的解耦,并且盡量做到性能計算統計相關程序不影響頁面本身的性能,在技術實現設計層我們把上面的指標做了一些分類,比如 同步計算指標(基礎業務同步指標、基礎性能資源同步指標)、異步計算指標(性能異步指標、后置異步指標)等。具體如下圖所示。

圖片

技術層面指標分類

下面詳細介紹一下一些關鍵邏輯是怎么處理的?各類性能指標具體是怎么計算的?下面列出了部分指標怎么獲取和計算的關鍵代碼。

SPA 項目的路由頁面的攔截關鍵邏輯:

const hackRouter = () => {
if (!window?.history?.pushState) {
return;
}
// 瀏覽器的歷史記錄發生變化時被觸發, 導航前進、后退
const oldOnPopState = window.onpopstate;
window.onpopstate = function(this: WindowEventHandlers, ...args: any[]): any {
const to = window.location.href;
const from = lastHref;
lastHref = to;
// 通知訂閱的回調
triggerHandlers('history', {
from,
to
});
if (oldOnPopState) {
try {
return oldOnPopState.apply(this, args);
} catch (e) {}
}
};

// history pushState 或 replaceState 觸發,通過 history api 方式
const wrapHistoryFn = (type: 'pushState'|'replaceState') => {
const originalHistoryFunction = window.history[type]
return function(this: History, ...args: any[]): void {
const url = args.length > 2 ? args[2] : undefined;
if (url) {
// coerce to string (this is what pushState does)
const from = lastHref;
const to = String(url);
lastHref = to;
// 通知訂閱的回調
triggerHandlers('history', {
from,
to
});
}
return originalHistoryFunction.apply(this, args);
};
};
window.history.pushState = wrapHistoryFn("pushState");
window.history.replaceState = wrapHistoryFn("replaceState");
}

性能基礎指標的獲取相關代碼:

// 獲取 PerformanceTiming 相關數據
export const getPerformanceTimingData = (task: TaskTypes) => {
if (!window?.performance?.timing) return {}
const { metrics } = task;
const { state } = task.ctx;
const ptiming = performance.timing;
// 默認為 -1 方便過濾無效值
const result = {
blankTime: -1,
dnsTime: -1,
httpTime: -1,
domTime: -1,
domReady: -1,
// ...
}
// 頁面加載狀態
if(state === 'pageload') {
// ...
// 白屏
result.blankTime = fix(ptiming.responseStart - ptiming.navigationStart);
// DNS查詢
result.dnsTime = fix(ptiming.domainLookupEnd - ptiming.domainLookupStart);
// HTTP請求
result.httpTime = fix(ptiming.responseEnd - ptiming.responseStart);
// 解析dom樹
result.domTime = fix(ptiming.domComplete - ptiming.domInteractive);
// DOMready
result.domReady = fix(ptiming.domContentLoadedEventEnd - ptiming.navigationStart)
// ...
}
// 路由切換狀態
if (state === 'navigation') {
// ...
}
return result
}

資源相關指標的數據獲取關鍵邏輯:

// 記錄
let performanceCursor: number = 0;
// 獲取當前頁面資源列表
export const startPerformance = (task: TaskTypes) => {
const { timeOrigin } = task.ctx;
if (!window.performance || !window.performance.getEntries || !timeOrigin) {
return;
}
// performanceEntries
const performanceEntries = performance.getEntries();
const pss = performanceEntries.slice(performanceCursor);
// 處理 各種 performanceEntry 資源
formatResourceEntries(task, pss);
performanceCursor = Math.max(performanceEntries.length - 1, 0);
}
export const formatResourceEntries = (task: TaskTypes, entries: PerformanceEntryList) => {
const { state, startTimestamp, timeOrigin } = task.ctx;
const { metrics } = task
entries.forEach(entry => {
const startTime = entry.startTime;
// console.log( timeOrigin, startTime, startTimestamp, timeOrigin + startTime < startTimestamp)
if (state === 'navigation' && timeOrigin + startTime < startTimestamp) {
return;
}
const baseStartTime = startTimestamp - timeOrigin;
switch (entry.entryType) {
case 'navigation':
// 處理 bodysize
// ...
case 'paint':
// 處理 paint 指標 fcp fp
// ...
case 'resource':
// 序列化各種資源, 如js/css/img/jsonp/ajax/fetch/iframe...
calcResource(entry, result, baseStartTime);
}
// ...
}

業務指標數據的獲取:

// 初始化基礎業務指標
export const initBaseData = (task: TaskTypes) => {
const { params = { backup: {} }, options = {} } = task;
// ...
Object.assign(params, {
pagetype: options?.pagetype || pagetype,
actiontype: options?.actiontype || actiontype,
appid: options?.appid || appid,
// and more ...
});
return task;
}

longTask 的記錄獲取:

function startLongTasks(): void {
const entryHandler = (entries: PerformanceEntry[]): void => {
for (const entry of entries) {
const startTime = entry.startTime
const duration = entry.duration;
const endTime = startTime + duration;
const longtask = {
name: `longtask-${++n}`,
startTime,
endTime,
duration
}
longTasks.push(longtask);
}
};
if(PerformanceObserver?.supportedEntryTypes?.includes('longtask')) {
// 注冊 longtask 異步任務
observe('longtask', entryHandler);
}
}

在實際項目統計時,發現一些性能指標算法的適用性問題需要注意:

  1. LCP 算法存在的問題。比如:觸發條件限制的問題,當檢測到用戶輸入時候 FMP算法會停止計算,就導致某些場景觸發不了(比如主要內容還沒顯示就點擊頁面)。白屏占位圖問題,頁面初始有較大的白屏占位圖時 即使后面被移除了,LCP 算法還會把它當作主要內容。
  2. FMP 算法不適合某些特殊場景。比如:2/3 是金剛位圖片布局,最下面 1/3 區域有一個瀑布流,由于FMP算法計算規則會導致統計時間在瀑布流請求之后展現后,就導致直觀上的頁面首屏時間變大。

數據可以計算并獲取了,那么如何進行友好的處理上報?

由于內部埋點提下不支持回話形式的分段上報,那么就需要在前端提前準備好所有需要需要上報的數據的處理,整體B端 SPA 項目性能數據處理的上報處理機制,以及同步任務數據、異步任務數據任務的處理流如下圖所示。

圖片

3. 上報數據的體積優化

在進行數據上報時,如果頁面的靜態資源加載 / ajax請求數量很多時,埋點上報請求接口的 body 會很大,導致請求耗時長而影響頁面本身的性能。因此針對 body 過大的問題,對一些資源的統計做了序列化處理。

比如:單條靜態資源的原始數據結構為:

const entry:PerformanceResourceTiming = {
"name": "https://xxx.zzz.com/yyy.css?v=5J1NDtbnnIr2Rc2SdhEMlMxD4l9Eydj88B31E7_NhS4",
"entryType": "resource",
"startTime": 1924.6000000238419,
"duration": 1400.5999999642372,
"initiatorType": "link",
"fetchStart": 1924.6000000238419,
"responseEnd": 3325.199999988079,
}

序列化之后,將各個關鍵數據合并成一個字符串,即:

// 將 entries 分類,并把單個entry 進行字符串化后,再將所有 css entry 合并
const cssEntry:string = 'https://xxx.zzz.com/yyy.css|1924|1924|3325'

可以發現系列化精簡后將 255個字符優化成了 42 個字符。

往往B端 SPA 項目靜態資源和請求多達幾十上百個,這樣序列化處理合并之后,能將埋點上報請求 body 體積減少數千個字節。當然了,如果服務支持編解碼,還可以通過其他更優的序列化方案進行 body 體積壓縮。

4. 數據存儲與處理

在對數據進行處理時,也遇到了一些問題。

每天上報的性能埋點數據存儲在哪里?

如何計算數據?如何擴展數據?如何查詢數據?

二次計算后的數據量依舊非常大,該怎么辦?

  • 原始性能數據通過SDK采集后,經過數據倉庫的清洗,存儲在Hadoop中。雖然Hadoop可以存儲PB級別的數據,但查詢速度較慢,不適合實時性能分析查詢。為了解決這個問題,我們嘗試將清洗后的數據復制一份存儲在MySQL中,但隨著數據量的增加,MySQL方案出現了許多問題。考慮到實際場景的并發量不會太高,我們最終選擇將明細數據存儲在ClickHouse中。
  • 雖然明細數據可以通過查詢ClickHouse獲取,但對于許多聚合計算得出的數據,如果仍然通過查詢并實時計算,效率并不理想。因此,在數據落庫后,我們通過定時任務預先計算一部分聚合數據,然后將其導入MySQL中。這種做法的好處在于,這部分預先計算好的數據可以進行查詢,用戶體驗更好,而且后續需要擴展時,只需要對聚合數據進行二次計算加工即可。目前,聚合數據使用了6個維度進行分組計算,這些維度也可以用于組合查詢,方便后續擴展。
  • 盡管聚合數據已經合并計算過,但由于多維度組合,數據量仍然非常龐大。隨著后續維度的擴展,整體數據量呈指數級增長。考慮到性能分析的周期性不會太長,我們決定只保留整體聚合數據7天,并進行分表處理。如果數據量激增,我們會采取將數據轉入TiDB中,并按日期進行分區存儲。

為了解決數據處理中的兩個核心問題,我們采用了這個完整的流程。在面對如此龐大的數據時,我們需要考慮它們存儲在何處。同時,我們也需要考慮如何查找和計算需要的指標。這個流程可以幫助我們更好地處理數據,提高效率。

此外,這個流程還有一個重要的作用,那就是保證數據的準確性和完整性。在數據處理過程中,我們需要遵循一定的規則和標準,以確保數據的可靠性。這樣才能讓我們在分析數據時得出正確的結論,更好的進行針對性的優化。

5. 性能查詢展示平臺

web平臺部分功能頁面展示如下:

圖片

歷史變化曲線

圖片

性能數據查詢

總結

在B端項目中,頁面性能統計是非常有必要的,因為可以幫助我們了解實際用戶的具體頁面的加載速度、用戶體驗,以便了解當前頁面的質量,并且為優化頁面性能提供方向,從而提高用戶滿意度。

責任編輯:武曉燕 來源: 大轉轉FE
相關推薦

2023-11-01 07:44:29

轉轉Flutter業務

2023-12-27 19:12:42

OLAP自助分析

2022-11-07 14:45:26

轉轉價格DDD

2022-12-15 08:35:01

用戶畫像平臺

2023-02-08 09:42:30

策略方式容量

2020-07-17 19:55:50

Vue前端性能優化

2023-03-29 08:33:03

倉儲自動化系統

2022-10-28 08:31:43

2023-08-24 08:11:39

斷路器監控報警

2023-03-02 08:32:41

2023-07-24 09:03:38

汽車之家頁面性能監控

2023-03-22 08:32:35

2023-03-02 08:54:32

2022-10-28 09:15:02

2021-01-08 09:40:40

優化VUE性能

2023-04-19 13:18:41

動態線程池平臺

2023-06-07 08:32:32

引擎技術while

2024-06-06 08:18:42

回收業務

2022-02-10 08:16:52

算法業務ODPS

2024-07-31 20:45:45

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品视频一区二区三区 | 狠狠撸在线视频 | 精一区二区| 欧美一区二 | 精品产国自在拍 | www.国产日本 | 中文字幕亚洲精品在线观看 | 一区二区久久 | 精品欧美一区二区三区免费观看 | 国产欧美日韩一区 | 国产精品一区二区三级 | 一级女毛片 | 国产做a爱片久久毛片 | 亚洲 欧美 日韩在线 | 日日夜夜天天 | 日韩高清中文字幕 | 久久蜜桃资源一区二区老牛 | 国产黄a一级 | 我要看免费一级毛片 | 国产精品美女久久久久久久网站 | 中文字幕1区2区3区 日韩在线视频免费观看 | 在线观看视频91 | 7777奇米影视 | 色接久久 | 在线色网 | 日韩中文字幕一区二区 | 在线观看成年视频 | 亚洲欧美日韩电影 | 久久亚洲美女 | 伊人网站视频 | 精品久久av| 久久久久国产一区二区三区 | 亚洲免费一区二区 | av网站免费 | 九九热这里 | 国产精品日韩欧美 | 91av大全 | 国产在线中文字幕 | 中文字幕一区二区三区在线乱码 | 国产精品久久久爽爽爽麻豆色哟哟 | 久久综合久久久 |