也許你有聽過一個問題,你這款 web 應(yīng)用性能怎么樣呀?你會回答什么呢?是否會優(yōu)于海量 web 應(yīng)用市場呢?本文就來整理下如何進行 web 性能監(jiān)控?包括我們需要監(jiān)控的指標、監(jiān)控的分類、performance 分析以及如何監(jiān)控。
但是,如何進行 web 性能監(jiān)控本身是一個很大的話題,文中只會側(cè)重一部分進行研究,某些內(nèi)容不是很全面。
前言:為什么需要監(jiān)控?
web 的性能一定程度上影響了用戶留存率,Google DoubleClick 研究表明:如果一個移動端頁面加載時長超過 3 秒,用戶就會放棄而離開。BBC 發(fā)現(xiàn)網(wǎng)頁加載時長每增加 1 秒,用戶就會流失 10%。
我們希望通過監(jiān)控來知道 web 應(yīng)用性能的現(xiàn)狀和趨勢,找到 web 應(yīng)用的瓶頸?某次發(fā)布后的性能情況怎么樣?是否發(fā)布后對性能有影響?感知到業(yè)務(wù)出錯的概率?業(yè)務(wù)的穩(wěn)定性怎么樣?
監(jiān)控什么?
首先我們需要知道應(yīng)該監(jiān)控些什么呢?有哪些具體的指標?
google 開發(fā)者提出了一種 RAIL 模型來衡量應(yīng)用性能,即:Response、Animation、Idle、Load,分別代表著 web 應(yīng)用生命周期的四個不同方面。并指出最好的性能指標是:100ms 內(nèi)響應(yīng)用戶輸入;動畫或者滾動需在 10ms 內(nèi)產(chǎn)生下一幀;最大化空閑時間;頁面加載時長不超過 5 秒。
我們可轉(zhuǎn)化為三個方面來看:響應(yīng)速度、頁面穩(wěn)定性、外部服務(wù)調(diào)用
- 響應(yīng)速度:頁面初始訪問速度 + 交互響應(yīng)速度
- 頁面穩(wěn)定性:頁面出錯率
- 外部服務(wù)調(diào)用:網(wǎng)絡(luò)請求訪問速度
1.頁面訪問速度:白屏、首屏?xí)r間、可交互時間
我們來看看 google 開發(fā)者針對用戶體驗,提出的幾個性能指標
這幾個指標其實都是根據(jù)用戶體驗,提煉出對應(yīng)的性能指標
1)first paint (FP) and first contentful paint (FCP)
首次渲染、首次有內(nèi)容的渲染
這兩個指標瀏覽器已經(jīng)標準化了,從 performance 的 The Paint Timing API 可以獲取到,一般來說兩個時間相同,但也有情況下兩者不同。
2)First meaningful paint and hero element timing
首次有意義的渲染、頁面關(guān)鍵元素
我們假設(shè)當一個網(wǎng)頁的 DOM 結(jié)構(gòu)發(fā)生劇烈的變化的時候,就是這個網(wǎng)頁主要內(nèi)容出現(xiàn)的時候,那么在這樣的一個時間點上,就是首次有意義的渲染。這個指標瀏覽器還沒有規(guī)范,畢竟很難統(tǒng)一一個標準來定義網(wǎng)站的主體內(nèi)容。
google lighthouse 定義的 first meaningful paint:https://docs.google.com/document/d/1BR94tJdZLsin5poeet0XoTW60M0SjvOJQttKT-JK8HI/view
3)Time to interactive
可交互時間
4)長任務(wù)
瀏覽器是單線程的,如果長任務(wù)過多,那必然會影響著用戶響應(yīng)時長。好的應(yīng)用需要最大化空閑時間,以保證能最快響應(yīng)用戶的輸入。
2.頁面穩(wěn)定性:頁面出錯情況
- 資源加載錯誤
- JS 執(zhí)行報錯
3.外部服務(wù)調(diào)用
- CGI 耗時
- CGI 成功率
- CDN 資源耗時
監(jiān)控的分類?
web 性能監(jiān)控可分為兩類,一類是合成監(jiān)控(Synthetic Monitoring,SYN),另一類是真實用戶監(jiān)控(Real User Monitoring,RUM)
合成監(jiān)控
合成監(jiān)控是采用 web 瀏覽器模擬器來加載網(wǎng)頁,通過模擬終端用戶可能的操作來采集對應(yīng)的性能指標,最后輸出一個網(wǎng)站性能報告。例如:Lighthouse、PageSpeed、WebPageTest、Pingdom、PhantomJS 等。
1. Lighthouse
Lighthouse 是 google 一個開源的自動化工具,運行 Lighthouse 的方式有兩種:一種是作為 Chrome 擴展程序運行;另一種作為命令行工具運行。Chrome 擴展程序提供了一個對用戶更友好的界面,方便讀取報告。通過命令行工具可以將 Lighthouse 集成到持續(xù)集成系統(tǒng)。
展示了白屏、首屏、可交互時間等性能指標和 SEO、PWA 等。
騰訊文檔移動端官網(wǎng)首頁測速結(jié)果:
2. PageSpeed
https://developers.google.com/speed/pagespeed/insights/
不僅展示了一些主要的性能指標數(shù)據(jù),還給出了部分性能優(yōu)化建議。
騰訊文檔移動端首頁測速結(jié)果和性能優(yōu)化建議:
3. WebPageTest
WebPageTest
給出性能測速結(jié)果和資源加載的瀑布圖。
4. Pingdom
注意:Pingdom 不僅提供合成監(jiān)控,也提供真實用戶監(jiān)控。
合成監(jiān)控方式的優(yōu)缺點:
優(yōu)點:
- 無侵入性。
- 簡單快捷。缺點:
- 不是真實的用戶訪問情況,只是模擬的。
- 沒法考慮到登錄的情況,對于需要登錄的頁面就無法監(jiān)控到。
二、真實用戶監(jiān)控
真實用戶監(jiān)控是一種被動監(jiān)控技術(shù),是一種應(yīng)用服務(wù),被監(jiān)控的 web 應(yīng)用通過 sdk 等方式接入該服務(wù),將真實的用戶訪問、交互等性能指標數(shù)據(jù)收集上報、通過數(shù)據(jù)清洗加工后形成性能分析報表。例如 FrontJs、oneapm、Datadog 等。
1. oneapm
https://www.oneapm.com/bi/feature.html
功能包括:大盤數(shù)據(jù)、特征統(tǒng)計、慢加載追蹤、訪問頁面、腳本錯誤、AJAX、組合分析、報表、告警等。
2. Datadog
https://www.datadoghq.com/rum/
3. FrontJs
功能包括:訪問性能、異常監(jiān)控、報表、趨勢等。這種監(jiān)控方式的優(yōu)缺點:
優(yōu)點:
- 是真實用戶訪問情況。
- 可以觀察歷史性能趨勢。
- 有一些額外的功能:報表推送、監(jiān)控告警等等。缺點:
- 有侵入性,會一定程度上響應(yīng) web 性能。
performance 分析
在講如何監(jiān)控之前,先來看看瀏覽器提供的 performance api,這也是性能監(jiān)控數(shù)據(jù)的主要來源。
performance 提供高精度的時間戳,精度可達納秒級別,且不會隨操作系統(tǒng)時間設(shè)置的影響。
目前市場上的支持情況:主流瀏覽器都支持,大可放心使用。
基本屬性
performance.navigation: 頁面是加載還是刷新、發(fā)生了多少次重定向performance.timing: 頁面加載的各階段時長
各階段的含義:
performance.memory:基本內(nèi)存使用情況,Chrome 添加的一個非標準擴展
performance.timeorigin: 性能測量開始時的時間的高精度時間戳
基本方法
performance.getEntries()
通過這個方法可以獲取到所有的 performance 實體對象,通過 getEntriesByName 和 getEntriesByType 方法可對所有的 performance 實體對象 進行過濾,返回特定類型的實體。
mark 方法 和 measure 方法的結(jié)合可打點計時,獲取某個函數(shù)執(zhí)行耗時等。
- performance.getEntriesByName()
- performance.getEntriesByType()
- performance.mark()
- performance.clearMarks()
- performance.measure()
- performance.clearMeasures()
- performance.now() ...
提供的 API
performance 也提供了多種 API,不同的 API 之間可能會有重疊的部分。
1. PerformanceObserver API
用于檢測性能的事件,這個 API 利用了觀察者模式。
獲取資源信息2. Navigation Timing API
https://www.w3.org/TR/navigation-timing-2/
performance.getEntriesByType("navigation");
不同階段之間是連續(xù)的嗎? —— 不連續(xù)
每個階段都一定會發(fā)生嗎?—— 不一定
- 重定向次數(shù):performance.navigation.redirectCount
- 重定向耗時: redirectEnd - redirectStart
- DNS 解析耗時: domainLookupEnd - domainLookupStart
- TCP 連接耗時: connectEnd - connectStart
- SSL 安全連接耗時: connectEnd - secureConnectionStart
- 網(wǎng)絡(luò)請求耗時 (TTFB): responseStart - requestStart
- 數(shù)據(jù)傳輸耗時: responseEnd - responseStart
- DOM 解析耗時: domInteractive - responseEnd
- 資源加載耗時: loadEventStart - domContentLoadedEventEnd
- 首包時間: responseStart - domainLookupStart
- 白屏?xí)r間: responseEnd - fetchStart
- 首次可交互時間: domInteractive - fetchStart
- DOM Ready 時間: domContentLoadEventEnd - fetchStart
- 頁面完全加載時間: loadEventStart - fetchStart
- http 頭部大?。簍ransferSize - encodedBodySize
3. Resource Timing APIhttps://w3c.github.io/resource-timing/
performance.getEntriesByType("resource");

// 某類資源的加載時間,可測量圖片、js、css、
XHRresourceListEntries.forEach(resource => {
if (resource.initiatorType == 'img') {
console.info(`Time taken to load ${resource.name}: `, resource.responseEnd - resource.startTime);
}});
這個數(shù)據(jù)和 chrome 調(diào)式工具里 network 的瀑布圖數(shù)據(jù)是一樣的。
4. paint Timing API
https://w3c.github.io/paint-timing/
首屏渲染時間、首次有內(nèi)容渲染時間5. User Timing API
https://www.w3.org/TR/user-timing-2/#introduction
主要是利用 mark 和 measure 方法去打點計算某個階段的耗時,例如某個函數(shù)的耗時等。
<head>
<script>
// 通常在head標簽尾部時,打個標記,這個通常會視為白屏?xí)r間
performance.mark("first paint time");
</script>
</head>
<body>
...
<script>
// get the first paint time
const fp = Math.ceil(performance.getEntriesByName('first paint time')[0].startTime);
</script>
</body>
6. High Resolution Time APIhttps://w3c.github.io/hr-time/#dom-performance-timeorigin
主要包括 now() 方法和 timeOrigin 屬性。
7. Performance Timeline APIhttps://www.w3.org/TR/performance-timeline-2/#introduction
總結(jié)
基于 performance 我們可以測量如下幾個方面:
mark、measure、navigation、resource、paint、frame。
let p = window.performance.getEntries();
重定向次數(shù):performance.navigation.redirectCount
JS 資源數(shù)量: p.filter(ele => ele.initiatorType === "script").length
CSS 資源數(shù)量:p.filter(ele => ele.initiatorType === "css").length
AJAX 請求數(shù)量:p.filter(ele => ele.initiatorType === "xmlhttprequest").length
IMG 資源數(shù)量:p.filter(ele => ele.initiatorType === "img").length
總資源數(shù)量: window.performance.getEntriesByType("resource").length
不重復(fù)的耗時時段區(qū)分:
- 重定向耗時: redirectEnd - redirectStart
- DNS 解析耗時: domainLookupEnd - domainLookupStart
- TCP 連接耗時: connectEnd - connectStart
- SSL 安全連接耗時: connectEnd - secureConnectionStart
- 網(wǎng)絡(luò)請求耗時 (TTFB): responseStart - requestStart
- HTML 下載耗時:responseEnd - responseStart
- DOM 解析耗時: domInteractive - responseEnd
- 資源加載耗時: loadEventStart - domContentLoadedEventEnd
其他組合分析:
- 白屏?xí)r間: domLoading - fetchStart
- 粗略首屏?xí)r間: loadEventEnd - fetchStart 或者 domInteractive - fetchStart
- DOM Ready 時間: domContentLoadEventEnd - fetchStart
- 頁面完全加載時間: loadEventStart - fetchStart
JS 總加載耗時:
const p = window.performance.getEntries();
let cssR = p.filter(ele => ele.initiatorType === "script");
Math.max(...cssR.map((ele) => ele.responseEnd)) - Math.min(...cssR.map((ele) => ele.startTime));
CSS 總加載耗時:
const p = window.performance.getEntries();
let cssR = p.filter(ele => ele.initiatorType === "css");
Math.max(...cssR.map((ele) => ele.responseEnd)) - Math.min(...cssR.map((ele) => ele.startTime));
npm install -g lighthouse

當頁面鏈接至使用 target="_blank" 的另一個頁面時,兩個頁面將在同一個進程上運行。 如果新頁面正在執(zhí)行開銷極大的 JavaScript,當前頁面性能可能會受影響。
另外,target="_blank" 也有一個安全漏洞。新的頁面可以通過 window.opener 訪問舊的窗口對象,甚至可以使用 window.opener.location = newURL 將舊頁面導(dǎo)航至不同的網(wǎng)址。
當設(shè)置rel="noopener"時chrome會在獨立的進程中打開新頁面,同時會阻止window.opener,因此不存在跨窗口訪問。
<a target="_blank" rel="noopener" >

轉(zhuǎn)原文:騰訊前端團隊是如何做web性能監(jiān)控的?https://mp.weixin.qq.com/s/y2jh7oT36XdmHM9fImLl_w