頁(yè)面性能對(duì)于用戶(hù)體驗(yàn)、用戶(hù)留存有著重要影響,當(dāng)頁(yè)面加載時(shí)間過(guò)長(zhǎng)時(shí),往往會(huì)伴隨著一部分用戶(hù)的流失,也會(huì)帶來(lái)一些用戶(hù)差評(píng)。性能的優(yōu)劣往往是同類(lèi)產(chǎn)品中勝出的影響因素,也是一個(gè)網(wǎng)站口碑的重要評(píng)判標(biāo)準(zhǔn)。
一、名稱(chēng)解釋
前端監(jiān)控一般分為合成監(jiān)控和真實(shí)用戶(hù)監(jiān)控。
1.1、合成監(jiān)控
合成監(jiān)控就是模擬用戶(hù)的使用場(chǎng)景,訪(fǎng)問(wèn)一個(gè)頁(yè)面,通過(guò)一些工具和規(guī)則去檢測(cè)頁(yè)面,提取一些性能指標(biāo),生成一份檢測(cè)報(bào)告,注重檢測(cè)。
合成監(jiān)控的優(yōu)缺點(diǎn):
| 優(yōu)點(diǎn) | 缺點(diǎn) |
|---|---|
| 實(shí)現(xiàn)簡(jiǎn)單,社區(qū)方案成熟 | 配置復(fù)雜,不能完全還原用戶(hù)真實(shí)場(chǎng)景 |
| 能采集到更豐富的數(shù)據(jù) | 登錄等場(chǎng)景需要單獨(dú)處理 |
| 不影響真實(shí)用戶(hù)的頁(yè)面訪(fǎng)問(wèn)性能 | 單次檢測(cè)數(shù)據(jù)不夠準(zhǔn)確 |
1.2、真實(shí)用戶(hù)監(jiān)控

真實(shí)用戶(hù)監(jiān)控是指用戶(hù)在頁(yè)面上訪(fǎng)問(wèn),訪(fǎng)問(wèn)時(shí)會(huì)產(chǎn)生各類(lèi)性能數(shù)據(jù),在用戶(hù)訪(fǎng)問(wèn)停止的時(shí)候,將這些性能數(shù)據(jù)傳輸?shù)椒?wù)端,進(jìn)行數(shù)據(jù)整理分析的過(guò)程,注重監(jiān)控。
真實(shí)用戶(hù)監(jiān)控的優(yōu)缺點(diǎn):
| 優(yōu)點(diǎn) | 缺點(diǎn) |
|---|---|
| 完全還原用戶(hù)真實(shí)場(chǎng)景 | 對(duì)用戶(hù)的訪(fǎng)問(wèn)性能有一定影響 |
| 登錄等場(chǎng)景無(wú)需單獨(dú)解決 | 無(wú)法采集完整的資源加載瀑布圖 |
| 數(shù)據(jù)樣本足夠大且真實(shí),數(shù)據(jù)價(jià)值高 | 無(wú)法可視化展示頁(yè)面加載過(guò)程 |
1.3、定義合適的性能指標(biāo)
- 首次內(nèi)容渲染時(shí)長(zhǎng)(First Contentful Paint, FCP)
頁(yè)面最新出現(xiàn)的內(nèi)容渲染時(shí)長(zhǎng) - 首次展現(xiàn)平均值(Speed Index, SI)
頁(yè)面內(nèi)容可見(jiàn)填充的速度 - 最大內(nèi)容繪制時(shí)間(Largest Contentful Paint, LCP)
頁(yè)面核心內(nèi)容呈現(xiàn)時(shí)間,不采用 loading 狀態(tài)的數(shù)據(jù) - 可交互時(shí)間(Time to Interactive, TTI)
用戶(hù)是否會(huì)體驗(yàn)到卡頓 - 總阻塞時(shí)間(Total Blocking Time, TBT)
主線(xiàn)程被阻塞的時(shí)間,無(wú)法作出輸入響應(yīng) - 累計(jì)布局樣式偏移(Cumulative Layout Shift, CLS)

二、為什么做
基于需要對(duì)公司的 Web 產(chǎn)品進(jìn)行性能優(yōu)化,在做性能優(yōu)化的同時(shí),優(yōu)化的衡量標(biāo)準(zhǔn)也不可或缺。在頁(yè)面開(kāi)發(fā)時(shí)觀察頁(yè)面的性能并不夠準(zhǔn)確,因?yàn)椴煌拈_(kāi)發(fā)設(shè)備性能表現(xiàn)不同,所伴隨的變量也較多,不能夠準(zhǔn)確的反映性能優(yōu)化效果,也無(wú)法觀察產(chǎn)品的性能變化趨勢(shì)。為什么自研呢,自研有以下好處:
(1)借助第三方的性能檢測(cè)服務(wù)往往不能保證檢測(cè)數(shù)據(jù)的安全性。
(2)第三方的性能檢測(cè)服務(wù)一般無(wú)法與公司內(nèi)部系統(tǒng)打通流程,一般無(wú)法自動(dòng)化檢測(cè)公司內(nèi)部產(chǎn)品。
(3)可以做一些自定義開(kāi)發(fā),比如根據(jù)產(chǎn)品特點(diǎn)調(diào)整不同的性能指標(biāo)權(quán)重,從而更準(zhǔn)確的計(jì)算分?jǐn)?shù)。
那么在檢測(cè)收集到了這么多的指標(biāo)數(shù)據(jù)后,頁(yè)面性能到底如何呢,如果你的老板問(wèn)你公司的產(chǎn)品頁(yè)面性能如何,你該如何回復(fù)呢?假設(shè)列舉一大堆時(shí)間指標(biāo)、偏移量等數(shù)據(jù),老板看到這些數(shù)值的時(shí)候可能就是一頭霧水,根本理解不了產(chǎn)品的頁(yè)面性能到底如何。那么自研可以針對(duì)產(chǎn)品類(lèi)型,給出一個(gè)統(tǒng)一的標(biāo)準(zhǔn),這樣就方便去對(duì)比各個(gè)產(chǎn)品的性能表現(xiàn)了。
三、怎么做
3.1、基礎(chǔ)依賴(lài)
下面是檢測(cè)系統(tǒng)的整體架構(gòu):

這里設(shè)計(jì)的性能檢測(cè)系統(tǒng)主要包含前端頁(yè)面和服務(wù)端,其中:
前端頁(yè)面展示性能檢測(cè)入口、檢測(cè)結(jié)果、性能趨勢(shì)、性能排行榜等。
服務(wù)端基于 Nestjs + Lighthouse + Puppeteer 實(shí)現(xiàn),通過(guò) Typeorm 操作 MySQL 數(shù)據(jù)庫(kù),記錄和查詢(xún)性能檢測(cè)數(shù)據(jù)。
另外輔助一些插件進(jìn)行定時(shí)監(jiān)測(cè)、結(jié)果通知等操作,實(shí)現(xiàn)自動(dòng)化檢測(cè),相比頁(yè)面開(kāi)發(fā)時(shí)通過(guò)開(kāi)發(fā)者工具中的 Lighthouse 檢測(cè)有以下好處:
(1)不用開(kāi)發(fā)者主動(dòng)觸發(fā);
(2)不會(huì)阻塞開(kāi)發(fā)過(guò)程,無(wú)需等待;
Lighthouse 用于檢測(cè) Web 網(wǎng)頁(yè)的性能,主要基于 4 個(gè)主要步驟實(shí)現(xiàn),分別是交互驅(qū)動(dòng)、性能數(shù)據(jù)收集、審計(jì)整理以及記錄。具體為:
(1)用戶(hù)在性能檢測(cè)入口輸入待檢測(cè)的頁(yè)面地址,點(diǎn)擊開(kāi)始檢測(cè),頁(yè)面通過(guò)接口調(diào)用性能檢測(cè)服務(wù)
(2)Lighthouse 遍歷當(dāng)前頁(yè)面的收集器方法并合成一個(gè)總的收集器方法以便于采集數(shù)據(jù)
(3)對(duì)上述采集到的性能數(shù)據(jù)進(jìn)行計(jì)算和評(píng)分
Lighthouse 主要提供六個(gè)收集器,通過(guò)以下六個(gè)收集器即可采集到和實(shí)際訪(fǎng)問(wèn)接近的性能數(shù)據(jù),每個(gè)收集器的功能不一,如下:
(1)收集 DOM 元素相關(guān)數(shù)據(jù)、DOM 節(jié)點(diǎn)最大深度、滾動(dòng)條等
(2)收集頁(yè)面內(nèi)的所有圖片資源,并記錄下每個(gè)圖片元素的寬高和定位等屬性
(3)收集相關(guān)指標(biāo),如:FCP、LCP、CLS 等
(4)收集 JS 事件監(jiān)聽(tīng)數(shù)量、JS 堆棧等
(5)收集頁(yè)面的所有請(qǐng)求,包括狀態(tài)碼、請(qǐng)求頭、響應(yīng)頭、請(qǐng)求方式等
(6)收集 window.performance 下的性能數(shù)據(jù),用于計(jì)算加載時(shí)間
Puppeteer 是 Chrome 團(tuán)隊(duì)提供的一個(gè)無(wú)界面 Chrome 工具,俗稱(chēng)無(wú)頭瀏覽器,通過(guò)提供的 API 可以控制 Node 端的 Chrome 工具進(jìn)行指定的操作。在這里設(shè)計(jì)的性能檢測(cè)系統(tǒng)中,由于 Lighthouse 進(jìn)行檢測(cè)時(shí)打開(kāi)的類(lèi)似于無(wú)痕窗口,沒(méi)有登錄信息,所以 Puppeteer 主要幫助我們實(shí)現(xiàn)模擬登錄。
當(dāng)檢測(cè)頁(yè)面需要登錄時(shí),分析出頁(yè)面屬于哪個(gè) devops 實(shí)例,然后通過(guò) Puppeteer 跳轉(zhuǎn)到對(duì)應(yīng)的登錄頁(yè)面,然后輸入用戶(hù)名、密碼、驗(yàn)證碼,待登錄完成后跳轉(zhuǎn)至正確的頁(yè)面,再進(jìn)行頁(yè)面性能檢測(cè)。如果登錄后還在登錄頁(yè),表示登錄失敗,則獲取錯(cuò)誤提示并拋出。
以下是檢測(cè)系統(tǒng)的一個(gè)流程圖:

3.2、關(guān)鍵代碼
// 開(kāi)始檢測(cè)
async run(urlDto: UrlDto): Promise<object> {
const start = new Date().getTime();
try {
const { url, loginUrl } = urlDto;
const needLogin = url.includes('devops') || loginUrl;
console.log(`本次檢測(cè)${needLogin ? '' : '不'}需要登錄`, url);
const runResult = needLogin
? await this.withLogin(urlDto)
: await this.withOutLogin(url);
// 保存檢測(cè)結(jié)果文件,便于預(yù)覽
const urlStr = url.replace(/http(s?):\/\//g, '').replace(/\//g, '');
fs.writeFileSync(`./static/${urlStr}-report.html`, runResult?.report);
// 性能數(shù)據(jù)
const performance = runResult?.lhr?.categories?.performance || {};
const data = {
...performance,
auditRefs: performance?.auditRefs?.filter((item) => item.weight),
};
// console.log(data);
console.log(`本次耗時(shí):${((new Date().getTime() - start) / 1000).toFixed(2)}s`);
return {
code: 200,
data,
message: `耗時(shí):${((new Date().getTime() - start) / 1000).toFixed(2)}s`,
};
} catch (error) {
return {
code: 401,
message: error,
};
}
}
3.3、檢測(cè)規(guī)則
系統(tǒng)除了支持手動(dòng)輸入網(wǎng)頁(yè)地址檢測(cè),也支持自動(dòng)檢測(cè)。為了便于統(tǒng)計(jì)每個(gè)子產(chǎn)品的真實(shí)表現(xiàn),每天凌晨自動(dòng)檢測(cè) 10 次,去掉最高分,去掉最低分,從其余分?jǐn)?shù)中選擇中位數(shù)作為每天的檢測(cè)評(píng)分。
性能檢測(cè)時(shí)的數(shù)據(jù)采集可能因?yàn)榫W(wǎng)頁(yè)服務(wù)的不穩(wěn)定性,導(dǎo)致有偏大或偏小的數(shù)據(jù),所以提供某個(gè)時(shí)間段某個(gè)指標(biāo)的直方圖來(lái)分析數(shù)據(jù)的基本特征。也會(huì)提供某個(gè)產(chǎn)品的整體分?jǐn)?shù)趨勢(shì),便于統(tǒng)計(jì)某個(gè)時(shí)間段內(nèi)該產(chǎn)品的性能變化,也可以提現(xiàn)性能優(yōu)化前后的效果。

四、寫(xiě)在后面
這篇文章簡(jiǎn)單介紹了下該性能檢測(cè)系統(tǒng)的初步設(shè)計(jì)、一些頁(yè)面性能的概念以及采用的技術(shù)點(diǎn),后續(xù)請(qǐng)關(guān)注《搭建自動(dòng)化 Web 頁(yè)面性能檢測(cè)系統(tǒng) —— 實(shí)踐篇》。