前端性能精進之優化方法論(二)——分析

  在上一節中曾提到過兩種性能監控:SYN 和 RUM,那麼對應的也有兩種分析:數據分析和實驗室分析。

  數據分析會通過採集上來的性能信息來剖析和定位可能存在的各種問題。

  實驗室分析會通過某個線上或本地的測試工具對頁面進行單點測試,得出性能分析報告。

  本文會對前者介紹一些分析實踐,後者會介紹一些比較有名的性能測試工具。

  數據分析的前端代碼已上傳至 shin-admin,後端代碼上傳至 shin-server

一、數據分析

  在將數據採集到後就需要立刻存儲,並且按百分位數計算後,需要定期計算和清理。

  各類圖表的輔助可以更好的定位到發生的性能問題。

1)存儲

  在將性能數據採集到後,就需要將它們存儲到數據庫中,例如 MySQL。

  爲了避免拖垮服務器,在服務端接收時會通過隊列來異步新增。

  以我當前公司的實踐爲例,將性能數據存儲到 web_performance 表中,表結構如下。

CREATE TABLE `web_performance` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `load` int(11) NOT NULL DEFAULT '0' COMMENT '頁面加載總時間',
  `ready` int(11) NOT NULL DEFAULT '0' COMMENT '用戶可操作時間',
  `paint` int(11) NOT NULL DEFAULT '0' COMMENT '白屏時間',
  `screen` int(11) NOT NULL DEFAULT '0' COMMENT '首屏時間',
  `measure` varchar(1000) COLLATE utf8mb4_bin NOT NULL COMMENT '其它測量參數,用JSON格式保存',
  `ctime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `day` int(11) NOT NULL COMMENT '格式化的天(冗餘字段),用於排序,20210322',
  `hour` tinyint(2) NOT NULL COMMENT '格式化的小時(冗餘字段),用於分組,11',
  `minute` tinyint(2) DEFAULT NULL COMMENT '格式化的分鐘(冗餘字段),用於分組,20',
  `identity` varchar(30) COLLATE utf8mb4_bin NOT NULL COMMENT '身份',
  `project` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '項目關鍵字,關聯 web_performance_project 表中的key',
  `ua` varchar(600) COLLATE utf8mb4_bin NOT NULL COMMENT '代理信息',
  `referer` varchar(200) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '來源地址',
  `referer_path` varchar(45) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '來源地址中的路徑',
  `timing` text COLLATE utf8mb4_bin COMMENT '瀏覽器讀取到的性能參數,用於排查',
  `resource` text COLLATE utf8mb4_bin COMMENT '靜態資源信息',
  PRIMARY KEY (`id`),
  KEY `idx_project_day` (`project`,`day`),
  KEY `idx_project_day_hour` (`project`,`day`,`hour`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='性能監控'

  referer_path 字段,用於分析指定頁面的性能。

  表中的 project 字段會關聯 web_performance_project 表(結構如下所示)中的 key。

CREATE TABLE `web_performance_project` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `key` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '唯一值',
  `name` varchar(45) COLLATE utf8mb4_bin NOT NULL COMMENT '項目名稱',
  `ctime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '1:正常  0:刪除',
  PRIMARY KEY (`id`),
  UNIQUE KEY `name_UNIQUE` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='性能監控項目';

  性能項目就是要監控的頁面,與之前不同,性能的監控粒度會更細,因此需要有個後臺專門管理這類數據。

  key 值是通過名稱得到 16 位 MD5 字符串,需要引入 Node.js 的 cryto 庫,如下所示。

const crypto = require('crypto');
const key = crypto.createHash('md5').update(name).digest('hex').substring(0, 16);

  可以對長期維護的網頁創建單獨的性能項目,而對於那些臨時活動可以共用一個項目。

2)百分位數

  均值會受極值的影響,從而讓它不夠準確,無法真實的反映出用戶的性能情況。

  故而選擇了百分位數來解決極值問題,例如前 95% 用戶的首屏時間在 2s 內,

  這種寫法也叫 TP95,表示 95 分位數,TP 是 Top Percentile 的縮寫。

  95 分位數是比較高的統計指標,意味着大多數的用戶都能享受到更好的性能體驗。

  爲了能看到變化趨勢,可以採用圖表的方式,例如折線圖,如下所示,橫座標可按天或小時。

  

3)定時任務

  每天可以選一個時間(例如凌晨三點),來統計昨天的日誌信息。

  例如將計算得到的統計信息以 JSON 格式(如下所示)存儲到數據庫表的一個字段中。

  這個字段可以是 TEXT 類型或更大的 MEDIUMTEXT,一天只插入一條記錄。

{
  hour: {
    x: [11, 14],
    load: ["158", "162"],
    ready: ["157", "162"],
    paint: ["158", "162"],
    screen: ["157", "162"]
  },
  minute: {
    11: {
      x: [11, 18, 30],
      load: ["157", "159", "160"],
      ready: ["156", "159", "160"],
      paint: ["157", "159", "160"],
      screen: ["156", "159", "160"]
    },
    14: {
      x: [9, 16, 17, 18],
      load: ["161", "163", "164", "165"],
      ready: ["161", "163", "164", "165"],
      paint: ["161", "163", "164", "165"],
      screen: ["161", "163", "164", "165"]
    }
  }
}

  還可以選一個時間來做數據清理,因爲沒有必要一直將這麼多的數據保留着。

4)資源瀑布圖

  通過資源瀑布圖可以查看當時的資源加載情況。

  在上報性能參數時,將靜態資源的耗時通過 getEntriesByType() 方法得到(如下所示),然後一起打包給服務器。

// 靜態資源列表
const resources = performance.getEntriesByType("resource");
const newResources: TypeSendResource[] = [];
resources && resources.forEach((value: PerformanceResourceTiming): void => {
    // 過濾 fetch 請求
    if (value.initiatorType === "fetch") return;
    // 只存儲 1 分鐘內的資源
    if (value.startTime > 60000) return;
    newResources.push({
      name: value.name,
      duration: rounded(value.duration),
      startTime: rounded(value.startTime)
    });
  });
obj.resource = newResources;

  由於我本地業務請求使用的是 XMLHTTPRequest,因此在代碼中會過濾掉 fetch 請求,只在上報監控數據時採用了 fetch() 函數。

  這可以根據實際情況來處理。在蒐集資源時,1 分鐘以外的都會捨棄,並且只記錄了資源名稱、耗時和開始時間。

  最終的效果如下圖所示,包含一個橫向的柱狀圖,並且在圖中會標註白屏、首屏、load 和 DOMContentLoaded 的時間點。

  

  這樣能對資源的加載做更直觀的比較,便於定位性能問題。

5)堆疊柱狀圖

  先將所有的性能記錄統計出來,然後分別統計白屏和首屏 1 秒內的數量、1-2 秒內、2-3 秒內、3-4 秒內、4+秒的數量,白屏的 SQL 如下所示。

SELECT COUNT(*) FROM `web_performance` WHERE `ctime` >= '2022-06-12 00:00' and `ctime` < '2022-06-13 00:00';
SELECT COUNT(*) FROM `web_performance` WHERE `paint` <= 1000 and `ctime` >= '2022-06-12 00:00' and `ctime` < '2022-06-13 00:00';
SELECT COUNT(*) FROM `web_performance` WHERE `paint` > 1000 and `paint` <= 2000 and `ctime` >= '2022-06-12 00:00' and `ctime` < '2022-06-13 00:00';
SELECT COUNT(*) FROM `web_performance` WHERE `paint` > 2000 and `paint` <= 3000 and `ctime` >= '2022-06-12 00:00' and `ctime` < '2022-06-13 00:00';
SELECT COUNT(*) FROM `web_performance` WHERE `paint` > 3000 and `paint` <= 4000 and `ctime` >= '2022-06-12 00:00' and `ctime` < '2022-06-13 00:00';
SELECT COUNT(*) FROM `web_performance` WHERE `paint` > 4000 `ctime` >= '2022-06-12 00:00' and `ctime` < '2022-06-13 00:00';

  算出後,分母爲總數,分子爲上述五個值,組成一張堆疊柱狀圖,類似於下面這樣,每種顏色代碼一個佔比。

  

  這樣就能直觀的看到優化後的性能變化了,可更快的反饋優化結果。

6)階段時序圖

  在將統計的參數全部計算出來後,爲了能更直觀的發現性能瓶頸,設計了一張階段時序圖。

  描繪出 TTFB、responseDocumentTime、initDomTreeTime、parseDomTime 和 loadEventTime 所佔用的時間,如下所示。

  橙色豎線表示白屏時間,黑色豎線表示首屏時間。移動到 id 或來源地址,就會提示各類參數。

  

二、實驗室分析

  成熟的性能優化工具不僅能給出網絡、渲染等信息,還能給出各種經過實踐的優化建議,讓我們的優化工作事半功倍。

1)Chrome DevTools

  Chrome 的 DevTools 是一款內置的開發者工具,可用於調試頁面、查看網絡請求、打印日誌等常規功能。

  還提供了 Performance 面板,專門用於性能分析,可查看性能參數的時間點、各階段的耗時、內存變化等。

  限於篇幅原因,本文只會重點講解 NetworkPerformance 兩塊面板。

  在 Network 中會呈現頁面中所有的網絡請求(可指定類型),並且會給出狀態碼、協議、請求瀑布圖等信息。

  

  藍線是 DOMContentLoaded(DCL) 事件觸發的時間點,紅線是 Load 事件觸發的時間點。

  No throttling 用於模擬網速,模擬 4G、3G 或 Offline 離線等網絡,在實際開發中很常用。

  Performance 需要點擊錄製按鈕後,纔會開始分析,如下圖所示,內容還是比較多的。

  

  在 Frames 中可以查看各個階段的頁面截圖,Timings 中可以查看到 FP、LCP 等性能參數的時間點。

  Main 指的是當前頁面,火焰圖中描繪了 JavaScript 的性能。

  例如 Parse HTML、Evaluate Script(加載 JavaScript)、Compile Script(執行 JavaScript)等。

  在 Summary 的環形圖中,可以看到火焰圖中各種顏色對應的操作,例如:

  • 藍色 Loading 表示加載中,對 HTML、CSS 等資源進行解析工作。
  • 黃色 Scripting 表示執行腳本,例如執行函數、觸發事件等。
  • 紫色 Rendering 表示渲染,包括 HTML 和 CSS 的變化,例如重繪或重排。
  • 綠色 Painting 表示繪製,將合成的圖層繪製到屏幕中。

  Performance 中的 Network 更注重時序和優先級,可在此查看資源加載是否符合預期。

  

  內存視圖是一個用不同顏色標註的折線圖(如下所示),包括 JavaScript 堆、DOM 節點數量、事件監聽器數量等信息。

  

  此處只是蜻蜓點水般介紹了下 Performance 的功能,詳細內容還可以去參考官方英語文檔

2)WebPageTest

  WebPageTest 是一款線上性能分析工具,通過佈置一些特定的場景進行測試,例如不同的網速、瀏覽器、位置等。

  測試完成後,會給出一份性能報告,包括優化等級、性能參數、請求瀑布圖、網頁幻燈片快照、視頻等。

  WebPageTest 的原理是將配置參數發送到後臺,然後通過瀏覽器相關的代理程序,啓動 Chrome、Firefox 或 IE。

  執行完畢後將數據回傳給後臺,後臺再將數據保存起來,最後通過各種形式(統計圖、表格等),將分析過的數據呈現給用戶。

  如果是新手,官方還提供了一份快速入門指南作爲參考。多年前曾對 WebPageTest 做過分析,有些內容仍然具有參考價值。

  在選擇完瀏覽器和地區後(如下圖所示),點擊 Start Test 就開始測試了。

  

  性能報告的第一部分是優化建議和各種指標,包括 LCP、FCP、CLS 等,如下所示。

  

  Speed Index 表示速度指數,衡量頁面內容填充的速度(越低越好),適合頁面優化前後的對比。

  在 Requests Details 中,呈現了請求信息,視圖包括資源瀑布圖、連接時序圖、請求耗時表、各條請求的頭信息。

  

  

  

  在 WebPageTest 的幻燈片視圖(Filmstrip View)中,在滑動滾動條時,下面會有根紅線對應這個時刻的資源載入情況。

  

3)Lighthouse

  Lighthouse 會對測試的網站進行打分,包括性能、可訪問性、最佳實踐、SEO 和 PWA 五個部分,並且會提供這五個部分的優化建議。

  測量的指標有 6 個,FCP、SI、LCP、CLS、TTI 和 TBT,如下所示。

  

  Lighthouse 的使用方法有多種,第一種是在 Chrome 的 DevTools 中選擇 Lighthouse 面板,不過要使用的話,得安裝代理。

  另一種使用方法是將 Lighthouse 下載到本地,安裝後使用命令來執行測試,如下所示。

lighthouse https://www.pwstrick.com --output html --output-path ./report/report.html

  Lighthouse 給出一些切實可行的優化建議,如下圖所示,在每條建議中,還會給出 Learn More 的鏈接,瞭解更多優化細節。

  

  雖然是英文的,但用翻譯軟件或自己閱讀都比較容易理解其含義。

  例如修改圖像格式、壓縮圖像、預加載影響 LCP 的圖像、延遲加載屏幕外的圖像、減少未使用的腳本、剔除阻塞渲染的資源等。

總結

  數據分析和實驗室分析是性能優化的兩塊重要組成部分。

  數據分析在採集到性能信息後,會先進行日誌存儲,然後按指定的百分位數進行數據整理,最後還會定期進行刪除。

  在管理界面提供了幾種視圖來更好的分析性能瓶頸,包括資源瀑布圖、堆疊柱狀圖和階段時序圖。

  在實驗室分析中,主要介紹了 3 款性能測試工具,包括 DevTools Performance、WebPageTest 和 Lighthouse。

  3 款軟件都非常優秀,可以幫助開發人員更快、更準的進行優化工作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章