我給網站作了一場性能手術

前言

風和日麗: 我正笑嘻嘻地抓着我炫酷的ikbc鍵盤瘋狂的敲着Bug.
晴天霹靂: 被拉進了一個羣,產品說我作的網站很卡,須要作性能優化.
難以置信: 我但是用尊貴的Vue3+Ts開發的呢 (手動狗頭).
十分抗拒: 迫於yin威,我給網站作了體檢和手術.

體檢

市面上的體檢套餐有不少種,但其實都是換湯不換藥.那藥(標準)是什麼呢?咱們會在下面說明.這裏我選擇了谷歌親兒子"燈塔"(LightHouse)進行性能體檢.javascript

體檢標準

爲何我說大多體檢套餐都是換湯不換藥呢?咱們先從燈塔的計分規則提及:css

lighthouse-score-calc.png

從上面中咱們能夠看到燈塔v6/v7版是經過幾種性能指標及不一樣權重來進行計分的.這幾種指標主要是根據PerformanceTimingPerformanceEntry API標準進行定義.市面上大多體檢套餐也是基於這些指標定製的.接下來咱們來了解下這些指標的含義吧.html

FCP (First Contentful Paint)

渲染第一個元素(文本、圖片、canvas...)的時間點

SI (Speed Index)

首屏展示時間

LCP (Largest Contentful Paint)

渲染可視區域內最大內容元素的時間點

TTI (Time to Interactive)

頁面資源加載成功並能響應用戶交互的時間點

TBT (Total Blocking Time)

FCP到TTI之間,主線程被long task(超過50ms)阻塞的時間之和

CLS (Cumulative Layout Shift)

累計佈局偏移值

FID (First Input Delay)

用戶第一次在頁面進行交互(點擊連接、按鈕、自定義js事件),到瀏覽器實際開始處理這個事件的時間

Core Web Vitals

談到用戶體驗與性能指標,順便提下Core Web Vitals.
2020年5月,Google針對網站使用體驗推出了一套核心指標標準(Core Web Vitals).由三項指標構成:vue

web-vitals.png

爲何不是別的指標呢 ? 由於這套標準主要從如下三個維度進行評估:java

  • [加載狀況] : LCP
  • [交互性] : FID
  • [視覺穩定性] : CLSwebpack

    如何檢視Core Web Vitals 指標 ?

    開發者可利用如下幾種工具對Core Web Vitals進行監測:
    Core-Web-Vitals.webpgit

因爲FID須要一個真實用戶的交互,因此沒法用實驗數據測試.爲了能在實驗數據下測試FID,一般會用TBT (Total Blocking Time).雖然他們測量的內容不一樣,但改善TBT一般也能改善FID.github

體檢結果

不檢不知道,一檢嚇一跳.6個"重要器官"涼了一半...是時候對它動個手術了!web

指標評分

market-optimize-before.png

改善建議

improve-advice.png

手術

手術方案

既然是性能手術,方案就主要以性能指標做爲維度,主要分爲如下幾個點:canvas

  • 視覺穩定性 (Cumulative Layout Shift)
  • 加載狀況 (Largest Contentful Paint)
  • TTI (Time to Interactive)
  • TBT (Total Blocking Time)
  • FCP (First Contentful Paint)

手術過程

視覺穩定性 (Cumulative Layout Shift)

  • 優化未設置尺寸的圖片元素

    改善建議裏提到了一項優先級很高的優化就是爲圖片元素設置顯式的寬度和高度,從而減小布局偏移和改善CLS.

image-size.png

<img src="hello.png" width="640" height="320" alt="Hello World" />
  • 自定義字體文件加載期間保持可見狀態

    改善建議裏提到使用CSS font-display屬性確保自定義字體文件在加載期間可見.

webfont-load.png

這是由於網站下載自定義字體文件須要一段時間,而不一樣瀏覽器此時的行爲是不一樣的.一些瀏覽器在加載自定義字體時會隱藏文字,這種稱之爲FOIT(Flash Of Invisible Text).而一些瀏覽器會顯示降級字體,這種狀況稱之爲FOUT(Flash Of Unstyled Tex).這兩種行爲會致使"字體閃爍問題",影響視覺穩定性 (CLS).

個人處理方法是直接設置font-display:swap;這個屬性能確保字體在加載時間可見.雖然仍是會引起FOUT,可是相比FOIT,FOUT對視覺穩定性的影響會小一些.

更好的方案應該是預加載(preload)字體文件.讓字體下載有更高几率趕在FCP以前,從而避免FOIT/FOUT.

@font-face {
     font-family: 'Hello-World';
     src: url('../font/Hello-World.otf') format('OpenType');
     /* swap:若是設定的字體還未可用,瀏覽器將首先使用備用字體顯示,當設定的字體加載完成後替換備用字體 */
     font-display:swap;
 }
  • 避免頁面佈局發生偏移

cls-ele.png

咱們產品中有一個頂部動態插入的元素,這個元素會致使網站總體佈局下移.從而形成了較大的佈局偏移.跟產品及ui py交易後,咱們友好地對這個元素進行了調整.將該元素脫離文檔流,採用固定定位的方式進行展現.從而解決該問題.
  • 避免非合成動畫

non-composited-animation.png

改善建議中提到應避免使用非合成動畫,非合成動畫會使得頁面變得混亂並增長CLS.關於這個優化建議我以爲應該具體場景具體分析,不該該"因噎廢食".畢竟目前能被composited的css屬性只有transform & opacity.固然這也在提醒咱們平時在作CSS動畫時應注意優化 (好比常見的使用transform替代top).

加載狀況 (Largest Contentful Paint)

  • 替換最大內容繪製元素

    在改善建議中,我發現網站的最大內容繪製元素是谷歌地圖中的一個圖塊元素.這也難怪LCP指標的數據表現不理想了,緣由: 鏈路過長 - 下載谷歌地圖Js sdk => 初始化谷歌地圖=> 繪製 .
因而,我決定對最大內容繪製元素進行修改,從而提高LCP時間.我喵了一眼 Largest Contentful Paint API 關於該元素類型的定義,將"目標"鎖定到了一個loading元素 (繪製成本低: 默認渲染,不依賴任何條件和判斷).通過我對該元素的尺寸動了手腳後(變大),該元素成功"上位".

TBT (Total Blocking Time) / TTI (Time to Interactive)

  • 異步加載谷歌地圖Js sdk
    原先加載谷歌地圖Js sdk是經過動態添加script標籤同步加載的.這樣作的缺點實際上是很明顯的:

    • Google Maps Js sdk 加載時機太晚,影響TTI表現和用戶體驗.
    • js引擎佔據主線程進行相關js執行.
      個人處理方案就是對谷歌地圖Js sdk進行異步加載.這裏須要注意的是script async/defer的區別,我使用的是defer進行異步加載(async加載完畢後會當即執行,阻塞主線程,影響DOM解析).
    <script
          src="//maps.googleapis.com/maps/api/js"
          type="text/javascript"
          defer
    ></script>
  • 優化構建bundle體積
    查看基於webpack-bundle-analyzer生成的體積分析報告我發現有兩個可優化的大產物:
    • lottie動畫庫

      站點只有一個動畫效果的實現用到該庫,跟產品、ui又一頓py後,咱們決定犧牲一點"視覺效果".移除lottie library,改用CSS3實現.
    • ant-design-vue中內置的momentjs依賴

      momentjs的語言包(locale)體積很是大,而站點並沒有國際化需求.因此這裏我直接使用 webpack IgnorePlugin對語言包進行忽略.
      通過優化,bundle體積(gizp前)由原來的1.8MB減少至1.3MB.

FCP (First Contentful Paint)

網站使用的是Vue作的客戶端渲染.這也意味着FCP過程會有點"漫長". (初始化Vue實例等一系列工做須要佔用主線程執行Js).這裏我"自做聰明"地在html文件添加了"透明文本佔位符",搶佔FCP時間.這個騷操做我我的認爲有點抖機靈,你們能夠選擇性無視...
<div id="app">
   <!-- 佔位符 -->
   <p style="color:#fff;">Hello World</p>
</div>

其餘

除了針對上面幾個指標維度進行優化外,我還作了幾點優化,這裏簡單提一下:

  • 優化DOM嵌套層級及數量
  • 減小沒必要要的接口請求
  • 使用translate替換top作位移/動畫

手術結果

說了那麼多"廢話",那手術結果究竟如何呢 ? 是華麗變身仍是"反向一Q日神仙"呢 ? 直接上圖:

90.png

經過上圖咱們能夠看到各項指標及評分都有質的飛躍,雖然我"不要臉地截了個最高分" (LightHouse每次評分會有波動,實際效果是由原來的50-70分漲到了70-90分) !!!

結語

平時咱們侃侃而談的性能優化點每每也是咱們最容易忽略的點.性能優化也絕非一蹴而就,須要咱們在平常開發中不斷去發現和優化.路漫漫其修遠兮...

若是你以爲個人文章對你有幫助,歡迎關注我一塊兒玩耍~

相關文章
相關標籤/搜索