2018 前端效能優化清單

sunshine小小倩發表於2018-02-28

前言:這篇文章我在掘金翻譯計劃中跟著一起翻譯的文章(感謝掘金翻譯),我翻譯了第三部分,然後校對了第二部分,這篇文章對於前端效能優化的技術還是比較新穎和全面的,所以決定自己閱讀一遍英文原文,然後又用思維導圖整理了下重點,英文原文還是挺長的,所以

  • 如果想要粗略的瞭解本文內容的可以直接檢視我總結的思維導圖
  • 如果看完思維導圖覺得本文對你有所幫助的,可以檢視我自己翻譯並根據其他人翻譯整理後的文章
  • 如果你英文閱讀沒有障礙,並且覺得我翻譯不好的,可以直接閱讀 英文原文

推薦大家時間充裕的話可以自己閱讀英文原文,此文是根據掘金翻譯的四篇文章 + 其他的翻譯文章 + 自己翻譯修改得出的。因為翻譯大家懂得,會有一些錯誤的地方,歡迎大家指出,本人不保證翻譯沒有錯誤,但已經盡力去翻譯了,所以歡迎大家直接閱讀英文原文!!歡迎大家直接閱讀英文原文!!歡迎大家直接閱讀英文原文!!

2018 前端效能優化清單思維導圖

我們都知道效能很重要,但是,我們是否真的一直都知道我們效能優化的瓶頸在哪?是代價昂貴的 JavaScript?是 web 字型很慢?還是比較大的圖片?或者是渲染速度過於遲緩?tree-shaking(無用程式碼移除)、scope hoisting(作用域提升)、code-splitting(按需載入)、 intersection observer 以及 clients hints、CSS containment、HTTP/2 和 service workers 這些技術都是有利於效能優化的。並且,最重要的是,我們要從哪裡開始提升我們的效能呢?並且要怎麼建立長久有效的效能機制。

譯者注:

  • tree-shakingtree-shaking 是 Webpack 2 引入的新功能,tree-shaking 是無用程式碼移除(DCE, dead code elimination)的一個方法,但和傳統的方法不太一樣。tree-shaking 找到需要的程式碼,灌入最終的結果;傳統 DCE 找到執行不到的程式碼,從 AST 裡清除。—— 如何評價 Webpack 2 新引入的 Tree-shaking 程式碼優化技術?
  • scope hoistingscope hoisting 是 Webpack 3 的新功能,又譯作“作用域提升”。Webpack 將所有模組都用函式包裹起來,然後自己實現了一套模組載入、執行與快取的功能,使用這樣的結構是為了更容易實現 Code Splitting(包括按需載入)、模組熱替換等功能。—— Webpack 3 的新功能:Scope Hoisting
  • code-splitting:對於大型的 web 應用而言,把所有的程式碼放到一個檔案的做法效率很差,特別是在載入了一些只有在特定環境下才會使用到的阻塞的程式碼的時候。Webpack有個功能會把你的程式碼分離成Chunk,後者可以按需載入。這個功能就是code-splitting。—— 在Webpack中使用Code Splitting實現按需載入
  • intersection observer:可以自動"觀察"元素是否可見,由於可見(visible)的本質是,目標元素與視口產生一個交叉區,所以這個 API 叫做"交叉觀察器"。—— IntersectionObserver API 使用教程
  • clients hints:自動響應式圖片 —— Automatic responsive images with Client Hints
  • CSS containment:新的 CSS 屬性 Containment 允許開發者限制瀏覽器樣式、佈局和 paint 工作的範圍。—— CSS Containment in Chrome 52
  • service workers:實現離線頁面 ——Service worker concepts and usage

在以前,效能優化經常是在專案完成才去考慮的,經常被推遲到專案的末期,這樣優化的範圍就會縮小,優化的內容通常是優化靜態檔案和調整一些伺服器配置檔案。但是現在,效能優化發生了很大的改變,這些是遠遠不夠的。

效能不僅僅是一個技術問題:重要的是,當將它放入工作流中的時候,做出的設計決策必須受效能的提示。效能必須被測量、監測和完善,而網路日益增長的複雜性也使得跟蹤效能的度量原來越難,因為度量會根據裝置、瀏覽器、協議、網路型別和延遲而顯著變化(CDN,ISP,快取,代理,防火牆,負載均衡器和伺服器都在效能上發揮作用)。

所以,我們就必須時刻記住效能優化 —— 從專案開始的時候直到網站最後釋出。所以我們就建立了一個效能優化列表。(希望是公正的和客觀的)2018 前端效能檢查表 —— 可以幫助你解決你網站的優化問題,例如,響應時間如何更快,流暢的使用者互動和不會流失使用者的頻寬。

(你也可以僅下載 2018 前端效能優化清單 的 PDF 版本(0.129 MB)或者下載 Apple Pages 版本(0.236 MB))

準備就緒:計劃和度量

微小的優化對於保持效能也是十分重要的,但是時刻記著我們必須要有一個明確的目標,可度量的目標將影響整個過程中的任何決策。這裡有幾種不同的模式,並且下面的方法都是從自身角度出發,所以你要儘早確認你自己優化的優先順序。

  1. 樹立效能優化意識

在很多團隊中,前端開發者確切的知道常見的潛在問題是什麼,應該用什麼樣的載入模組來修復。然而,只要開發或者設計那邊有一方跟市場的意見不合,效能就不會長時間持續下去。所以,研究使用者常見的抱怨的地方,看看提高效能如何幫助解決這些常見問題。

無論是在移動裝置上還是在桌面上都需要執行效能實驗並測量結果。 它會建立一個用真實資料為你們公司量身定製的案例研究。此外,要善於使用 WPO Stats 上釋出的案例研究和實驗中的資料,可以幫助提高你對效能和業務之前潛在關係的認識,以及效能對使用者體驗和業務度量的影響。僅僅說明績效是不夠的 - 你還需要建立一些可衡量和可追蹤的目標來觀察效能。

  1. 目標:至少要比你最快的競爭對手還快 20%

根據 心理學研究,如果你想要使用者感覺你的網站比你競爭者的網站快,那麼你至少需要比它們快 20%。研究你的主要競爭者,收集他們在手機和桌面上的效能,並且設定一個你能超過他們的臨界值。要獲取準確的結果和目標,首先要研究你的分析結果,看看你的使用者都在做什麼。你可以模擬第百分之九十的使用者經驗進行測試,收集資料,然後建立一個 表格,然後再減去 20% 就是你的目標(即 效能預算)。現在你在測試之前就有了一些可度量的資料。

如果你比較在意預算並嘗試去用最小的指令碼來獲得最快的互動時間,那麼你就開始了你的“前端優化之旅”。Lara Hogan 關於如何使用效能預算來處理設計的 指南 可以為設計人員提供有用的指引,而 效能預算計算器Browser Calories 裡都可以幫助建立預算(感謝 Karolina Szczur 提供)。

[Brad Frost 構建的效能預算](http://bradfrost.com/blog/post/performance-budget-builder/)

除了效能外,還要考慮對你業務最有利的客戶的行為。 設定和討論關鍵行動的可接受時間閾值,並設定一個整個團隊已經達成一致的 “UX ready”使用者時間標記。 大部分情況下,使用者的行為涉及許多不同部門,因此,按照設定的可接受的時間安排會避免很多問題。確保額外的資源和功能的額外成本其他成員都是可見和知曉的。

此外,正如 Patrick Meenan 所建議的那樣,最好在設計的過程中設定一個載入佇列並且要知道這些順序會存在哪些利弊。你需要優先處理更重要的優先順序,並且定義一個它們優先順序的順序,你也應該清楚哪些是可以推遲的。理想的情況,該佇列也應該反映出 JS 和 CSS 的載入順序,只要設計好了,那麼在構建過程中處理它們就會很簡單了。另外,還需要考慮的是在頁面還在載入時頁面的“中間狀態”(例如,當 web 字型沒有載入完時)。

計劃計劃計劃,重要的事情說三遍!如果早期進行優化那麼會很容易實現目標,但是沒有計劃或者沒有制定切合實際的、為公司量身定製的業績目標那麼就很難保持效能。

  1. 選擇正確的度量

不是所有的度量都一樣重要。 研究哪些度量對於你的應用程式最重要:通常,這與你能夠以多快的速度開始渲染最重要的畫素(以及它們的效果)以及如何為這些渲染畫素提供輸入最快的響應速度有關。 這可以幫助你為後續的工作提供最佳的優化結果。 不管怎樣,不要專注於整個頁面載入時間(例如 onLoadDOMContentLoaded 時間),而是要優先按照使用者可以感知到的頁面載入。 也就是說要關注一組稍微不同的度量。

首次有內容渲染,視覺完整和可互動時間之間的區別。來自於:@denar90

你可以參考下面這樣度量:

  • 首次有效渲染(FMP,是指主要內容出現在頁面上所需的時間),
  • 重要渲染時間(頁面最重要部分渲染完成所需的時間),
  • 可互動時間(TTI,是指頁面佈局已經穩定,關鍵的頁面字型已經可見,主程式可以足夠的處理使用者的輸入 —— 基本的時間標記是,使用者可以在 UI 上進行點選和互動),
  • 輸入響應,介面響應使用者操作所需的時間,
  • Speed Index,測量填充頁面內容的速度。 分數越低越好,
  • 自定義度量,由你的業務需求和使用者體驗來決定。

Steve Souders 對 每個度量都進行了詳細的解釋。在許多情況下,根據你的應用程式的使用場景,可互動時間和輸入響應 會是最關鍵的。但這些度量可能會不同:例如,對於 Netflix 電視的使用者介面來說,關鍵輸入響應、記憶體使用和可互動時間更為重要。

  1. 從具有代表性的使用者使用的裝置收集資料

為了收集準確的資料,我們需要儘可能全的選擇要測試的裝置。最好是 Moto G4,或者一款中檔的三星裝置又或者是一個 Nexus 5X 這樣的普通的裝置。如果你手邊沒有裝置,可以使用節流 CPU(5× 減速)來限制網速(例如,150 ms 的往返時延,1.5 Mbps 以下和0.7 Mbps 以上)實現在桌面裝置上模擬移動裝置的體驗。最終,切換到常規的 3G,4G 和 Wi-Fi。為了使效能體驗的影響更明顯,你甚至可以使用 2G 一個節流的 3G 網路,以便進行更快的測試。

因為週二是一週中最慢的一天。Facebook推出了[2G Tuesdays](https://www.theverge.com/2015/10/28/9625062/facebook-2g-tuesdays-slow-internet-developing-world),以提高對低速連線的能見度和靈敏度。([圖片來源](http://www.businessinsider.com/facebook-2g-tuesdays-to-slow-employee-internet-speeds-down-2015-10?IR=T))

幸運的是,有很多優秀的選項可以幫助你自動收集資料,並根據這些度量衡量你的網站在一段時間內的效能。 請記住,良好的效能度量是需要被動和主動監控工具的組合:

  • 被動監測工具,可以根據請求來模擬使用者互動(綜合測試,如LighthouseWebPageTest
  • 主動監測工具, 是那些不斷記錄和評價使用者互動行為的(真正的使用者監控,如 SpeedCurveNew Relic —— 這兩種工具也提供綜合測試)

前者在開發過程中特別有用,因為它可以在使用產品時持續跟蹤。 後者在長期維護非常有用,因為它可以幫助你瞭解在實際訪問站點時發生的效能瓶頸。通過使用導航定時、資源定時、繪圖定時、長時間任務等內建的 RUM API,被動和主動效能監視工具一起提供應用程式效能的完整畫面。 例如,你可以使用PWMetricsCalibreSpeedCurvemPulseBoomerangSitespeed.io,這些都是效能監測工具的絕佳選擇。

注意:選擇瀏覽器外部的網路級別的限制器總是比較安全的,例如 DevTools 由於實現的方式而存在與 HTTP/2 推送互動的問題(感謝Yoav!)。

[Lighthouse](https://developers.google.com/web/tools/lighthouse/)一個整合在 DevTools 的效能檢測工具。

  1. 與你的同事分享效能清單

為了避免誤解,要確保你團隊裡的每個同事都對清單很熟悉。每個決策都對效能有影響。專案將極大地受益於前端開發人員正確地將效能價值傳達給整個團隊。從而使每個人都對它負責,而不僅僅是前端開發人員。根據績效預算和清單中定義的優先順序來設計決策。

[RAIL](https://developers.google.com/web/fundamentals/performance/rail),以使用者為中心的效能模型。

制定現實的目標

  1. 控制響應時間在 100ms 內,控制幀速在 60 幀/秒

為了讓互動感覺起來很順暢,介面有 100ms 來響應使用者的輸入。任何比它長的時間,使用者都會認為該應用程式很慢。RAIL,一個以使用者為中心的效能模型會為你提供健壯的目標。為了讓頁面達到小於 100ms 的響應,頁面必須要在小於 50ms 前最遲將控制權返回給主執行緒。預計輸入延遲時間 告訴我們,如果我們能達到這個門檻,在理想情況下,它應該低於 50ms。對於像動畫這樣效能消耗比較大的地方,最好的做法是,在能夠優化的地方,儘量優化到極致;在不能優化的地方,讓效能開銷降至最低。

同時,每一幀動畫應該要在 16 毫秒內完成,從而達到 60 幀每秒(1秒 ÷ 60 = 16.6 毫秒) —— 最好可以在 10 毫秒完成。因為瀏覽器需要時間將新框架繪製到螢幕上,你的程式碼應該在觸發 16.6 毫秒以內完成。保持樂觀和明智地利用空閒時間。顯然,這些目標適用於執行時的效能,而不是載入效能。

  1. 速度指標(SpeedIndex) < 1250,3G 上互動時間 < 5s,關鍵檔案大小 < 170Kb(SpeedIndex < 1250, TTI < 5s on 3G, Critical file size budget < 170Kb)

雖然這可能很難實現,一個好的最終目標是首次有效渲染低於 1 秒並且速度指標的值低於 1250。因為我們是以 200 美金為基準的 Android 手機(如 Moto G4)和一個緩慢的 3G 網路上,模擬 400ms 的往返延時和 400kb 的傳輸速度,所以我們的目標是可互動時間低於 5s,並且再次訪問的時間低於 2s。

請注意,當談到可互動時間時,最好來區分一下首次互動和連續互動以避免對它們之間的誤解。前者是在主要內容已經渲染出來後最早出現的點(視窗至少需要 5s,頁面才開始響應)。後者是期望頁面可以一直進行輸入響應的點。

HTML 的前 14~15kb 載入是是最關鍵的有效載荷塊 —— 也是第一次往返(這是在400 ms 往返延時下 1 秒內所得到的)預算中唯一可以交付的部分。一般來說,為了實現上述目標,我們必須在關鍵的檔案大小內進行操作。最高預算壓縮之後 170 Kb(0.8-1MB解壓縮),它已經佔用多達 1s (取決於資源型別)來解析和編譯。稍微高於這個值是可以的,但是要儘可能地降低這些值。

儘管如此,還是可以提高繫結的規模預算。例如,你可以在瀏覽器主執行緒的活動中設定效能預算,例如,在開始渲染前的繪製時間或者跟蹤前端 CPU 。像 CalibreSpeedCurveBundlesize 這些工具可以幫助你保持你的預算控制,並整合到你的構建過程。

[本來就很快的:現代化載入的最佳實踐](https://speakerdeck.com/addyosmani/fast-by-default-modern-loading-best-practices) 來自 Addy Osmani(幻燈片 19)

定義環境

  1. 做好構建工具的選型以及搭建好(適合自己的)構建工具

現如今不要太在意那些很酷的技術棧。根據你的專案使用你的構建工具,無論是 Grunt,Gulp,Webpack,Parcel,還是工具間的組合。只要你能快速的得到結果,並且保證你的構建過程沒問題。那麼,你就可以選擇該構建工具。

  1. 漸進式增強

安全的選擇是將 漸進式增強 作為前端架構和專案部署的指導原則。首先設計和構建核心體驗,然後為有能力的瀏覽器使用高階特性增強體驗,創造 彈性 體驗。如果你的網站是在一個網路不佳的並且有個糟糕的螢幕上糟糕的瀏覽器上執行,速度還很快的話,那麼,當它執行在一個快速網路下快速的瀏覽器的機器上,它只會執行得更快。

  1. 選擇一個強大的效能基準

有這麼多未知因素影響載入 —— 網路、熱保護、快取回收、第三方指令碼、解析器阻塞模式、磁碟的讀寫、IPC jank、外掛安裝、CPU、硬體和記憶體限制、L2/L3快取、RTTS、影象、Web字型載入行為的差異 —— JavaScript 的代價是最大的,web 字型阻塞預設渲染和圖片的載入消耗了大量的記憶體。隨著效能瓶頸從 伺服器端轉移到客戶端,作為開發人員,我們必須更仔細地考慮所有這些未知因素。

在 170kb 的預算中,已經包括了關鍵路徑的 HTML / CSS / JavaScript、路由器、狀態管理、實用程式、框架和應用程式邏輯,我們必須徹底檢查網路傳輸成本,解析/編譯時間和執行時間來選擇我們的框架

[現代化載入的最佳實踐](https://speakerdeck.com/addyosmani/fast-by-default-modern-loading-best-practices)來自 Addy Osmani(幻燈片18、19)。

正如 Seb Markbage 所 指出的,測量框架的啟動成本的好方法是首先渲染檢視,再刪除它,然後再渲染,因為它可以告訴你框架是如何擴充套件的。

第一次渲染傾向於預熱一堆編譯遲緩的程式碼,當它擴充套件時,更大的分支可以從中受益。第二次渲染基本上是仿效頁面上的程式碼重用是如何隨著頁面複雜度的增長來影響效能特徵。

並不是每個專案都需要框架。事實上,某些專案 可以完全移除某些框架並從中受益。一旦選擇了一個框架,你最少會使用好幾年。所以,如果你需要使用它,確保你的選擇是經過深思熟慮的 並且 對其完全瞭解。在進行選擇前,至少要考慮總大小的成本 + 初始解析時間:輕量級的選項像 PreactInfernoVueSvelte 或者 Polymer 都做得很好。框架的大小基線將為你的應用程式程式碼定義約束條件。。

JavaScript 解析成本可能有很大差異。[現代化載入的最佳實踐](https://speakerdeck.com/addyosmani/fast-by-default-modern-loading-best-practices) 來自Addy Osmani (幻燈片 10)。

請記住,在移動裝置上,與臺式計算機相比,預計速度會有 4-5 倍的下降。因為移動裝置的 GPU、CPU、記憶體及電池特性都不同。在手機上的解析時間比桌面裝置的要高 36%。所以總在一個最能代表普遍使用者的平均的裝置上測試

不同的框架將會對效能產生不同的影響,並且需要不同的優化策略。因此,你必須清楚地瞭解你所依賴的框架的所有細節。當建立一個 web 應用的時候,參考 PRPL 模式應用程式 shell 體系結構。這個想法很簡單: 用最少的程式碼來將初始路由的互動快速呈現,然後使用 service worker 進行快取和預快取資源,然後使用懶載入非同步載入所需的路由。

PRPL Pattern in the application shell architecture

PRPL 代表的是保持推送關鍵資源,渲染初始路由,預快取剩餘路由和延遲載入必要的剩餘路由。

Application shell architecture

應用程式 shell 是最小的 HTML、CSS 和 JavaScript 驅動的使用者介面。

  1. 你的專案需要使用 AMP 和 Instant Articles 麼?

譯者注:AMP 即加速移動頁面,是一種製作可快速載入的輕量型網頁的方法,特別適合於移動裝置。

依賴於你的組織優先順序和戰略,你可以考慮使用谷歌的 AMP 和 Facebook 的 Instant Articles 或者蘋果的 Apple News。如果不使用它們,你也可以實現很好的效能,但是 AMP 確實提供了一個免費的內容分發網路(CDN)的效能框架,而 Instant Articles 將提高你在 Facebook 上的知名度和表現。

對於使用者而言,這些技術主要的優勢是確保效能,所以有時他們更喜歡 AMP-/Apple News/Instant Pages 連結,而不是“常規”和潛在的臃腫頁面。對於那些以內容為主的網站,主要處理很多第三方法內容,這些選擇極大地加速渲染的時間。

對於站長而言,這些樣式在各個平臺可發現性並且 增強在搜尋引擎中的可見性。你也可以重新使用 AMP 作為你的 PWA 資料來源來構建漸進的 Web AMPs。有什麼缺點呢?顯然,在一個有圍牆的區域裡,開發者可以創造並維持與內容分離的單獨版本,防止 Instant Articles 和 Apple News 沒有實際的URLs。(謝謝,Addy,Jeremy

  1. 合理使用 CDN

根據你擁有的動態資料量,你可以將部分內容外包給靜態站點生成工具 如 Jekyll、Hexo 生成的靜態檔案,接著把靜態檔案推到 CDN 中,最後 CDN 只是提供靜態檔案的靜態版本。所以這樣就可以避免發起對資料庫的讀寫請求。你甚至可以選擇一個基於 CDN 的靜態主機平臺,(這樣就可以)通過給頁面新增可互動元件的方式來豐富你的頁面,並以此作為效能提升的標誌 (JAMStack)。

請注意,CDN 也是可以託管並解除安裝(offload)動態內容的,所以我們們沒有必要把 CDN 的服務範圍限定在靜態資源。(另外需要你記住的是),不管你的 CDN 是否執行內容壓縮(GZip)、內容轉換、HTTP/2 傳輸以及 ESI(一種標記語言,可以用它把網頁劃分為單獨的可快取的實體)等操作,我們還是需要複核上述操作的,這是因為上述操作不僅會在 CDN 的 edge 處(伺服器最接近使用者的地方)聚合頁面中的靜態以及動態內容,也還會執行其它任務。

構建優化

  1. 分清輕重緩急

你應該知道優先處理什麼。執行你所有靜態資源(JavaScript、圖片、字型、第三方指令碼和頁面中“昂貴的”模組,比如:輪播圖、複雜的圖表和多媒體內容),並將它們劃分成組。

先搞清楚資源(assets)可以分為幾類,大致可以分為:

  • 針對傳統的瀏覽器,定義基本的核心功能(比如:完全可訪問的核心內容)
  • 針對多功能瀏覽器提升功能(比如:豐富多彩的,完美的體驗)
  • 其他資源(不是絕對需要而且可以被延遲載入的資源,如 Web 字型、不必要的樣式、旋轉木馬指令碼、視訊播放器、社交媒體按鈕、大型影象)

我們在“Improving Smashing Magazine's Performance”上釋出了一篇文章,上面詳細描述了該方法。

  1. 考慮使用“cutting-the-mustard”模式

雖然這個技術已經很老了,但我們仍然可以使用 cutting-the-mustard 技術 使傳統瀏覽器使用核心功能並增強對現代瀏覽器的體驗。嚴格限制載入的資源:優先載入核心功能,然後是提升的,最後是其他的。注意:該技術可以從瀏覽器版本中推斷出裝置功能,而現在我們已經不再這樣做了。

例如:在發展中國家,廉價的安卓手機主要執行 Chrome,他們的記憶體和 CPU 有限。PRPL 模式 就是一個好的選擇。最終,使用 Device Memory Client Hints Header,我們就能夠更可靠地識別出低端裝置。現在,只有在 Blink 中才支援 header (Blink 支援client hints)。因為裝置儲存也有一個在 Chrome 中可以呼叫的 JavaScript API,一種選擇是基於 API 的特性檢測,只在不支援的情況下回退到“符合標準”技術(謝謝,Yoav!)。

  1. 減少解析JavaScript 的成本

當我們處理單頁面應用時,在你的頁面渲染之前你需要初始化應用。尋找模組和技術加快初始化渲染時間(例如:如何除錯 React 效能,以及如何提高 Angular 效能),因為大多數效能問題來自於啟動應用程式的初始解析時間。

JavaScript 有成本,但不一定是檔案大小影響效能。解析和執行時間的不同很大程度依賴裝置的硬體。在一個普通的手機上(Moto G4),僅解析 1MB (未壓縮的)的 JavaScript 大概需要 1.3-1.4 秒,會有 15 - 20% 的時間耗費在手機的解析上。在執行編譯過程中,只是用在 JavaScript 準備平均需要 4 秒,在手機上頁面展示其主要內容所需的時間(First Meaningful Paint)需要 11 秒。解釋:在低端移動裝置上,解析和執行時間可以輕鬆提高 2 至 5 倍

Ember 最近做了一個實驗,使用二進位制模板(binary templates )巧妙的避免解析開銷的方式。這些模板不需要解析。(感謝,Leonardo!

這就是檢查每個 JavaScript 依賴性的關鍵,像 webpack-bundle-analyzerSource Map ExplorerBundle Buddy 這樣的工具可以幫助你完成這些。度量 JavaScript 解析和編譯時間。Etsy 的 DeviceTiming,一個小工具可以讓你的 JavaScript 測量任何裝置或瀏覽器上的解析和執行時間。重要的是,雖然檔案的大小重要,但它不是最重要的。解析和編譯時間並不是隨著指令碼大小增加而線性增加

Webpack Bundle Analyzer visualizes JavaScript dependencies.

  1. 你使用 ahead-of-time 編譯器麼?

使用 ahead-of-time 編譯器減輕從客戶端服務端的渲染 的開銷,因此快速輸出有用的結果。最後,考慮使用 Optimize.js 通過包裝可快速呼叫的函式來實現(在app初始時能夠)快速載入(儘管,它可能不需要)。

  1. 你使用 tree-shaking、scope hoisting、code-splitting 麼

Tree-shaking 是一種清理構建過程的方法通過只載入生產中實際使用的程式碼並清除 在 Webpack 中 未使用的 import。使用 Webpack 3 和 Rollup,我們還可以使用 scope hoisting(作用域提升)scope hoisting 允許工具檢測哪些 import 可以被提升或者可以轉換成一個行內函數。有了 Webpack 4,你現在可以使用 JSON Tree ShakingUnCSS 或者 Helium 可以幫助你去刪除未使用 CSS 樣式。

而且,你需要考慮 如何編寫有效的 CSS 選擇器 以及 如何避免編寫臃腫和開銷浪費的樣式。你也可以使用 Webpack 縮短類名和在編譯時使用獨立作用域來 動態地重新命名 CSS 類

Code-splitting 是 Webpack 的另一個特性,可將你的程式碼分解為按需載入的“塊”。並不是所有的 JavaScript 都是必須下載、解析和編譯的。一旦你在程式碼中確定了分割點,Webpack 會處理這些依賴關係和輸出檔案。在應用傳送請求的時候,這樣基本上確保初始的下載足夠小並且實現按需載入。另外,考慮使用 preload-webpack-plugin 獲取程式碼拆分的路徑,然後使用 <link rel="preload"> or <link rel="prefetch"> 提示瀏覽器預載入它們。

在哪裡定義分離點?通過追蹤哪些 CSS/JavaScript 塊被使用和哪些沒有被使用。Umar Hansa 解釋了你如何使用 Devtools 的 Code Coverage 來實現。

如果你沒有使用 Webpack,那麼相比於 Browserify 的輸出結果, Rollup 的輸出更好一些。當使用 Rollup 時,推薦你瞭解下 Rollupify,它可以將 ECMAScript 2015 modules 轉化為一個大的 CommonJS module —— 因為小的模組會有令人驚訝的高效能開銷(取決於打包工具和模組載入系統的選擇)。

Addy Osmani 的“預設快速:現代化載入的最佳實踐”

Addy Osmani 的從快速預設:現代載入的最佳實踐。幻燈片76。

最後,隨著現代瀏覽器對 ES2015 支援越來越好,考慮 使用babel-preset-env 只轉換現代瀏覽器不支援的 ES2015+ 的特性。然後設定兩個構建,一個為 ES6 一個為 ES5。我們可以 使用script type="module" 讓具有 ES 模組瀏覽器支援載入檔案,而老的瀏覽器可以載入傳統的 script nomodule

對於 loadsh,使用 babel-plugin-lodash 將只載入你僅在原始碼中使用的模組。這可能會為你節省相當多的 JavaScript 負載。

  1. 利用你使用的 JavaScript 引擎對其進行優化

研究 JavaScript 引擎在使用者基礎中佔的比例,然後探索優化它們的方法。例如,當優化的 V8 引擎是用在 Blink 瀏覽器,Node.js 執行和 Electron 的時候,對每個指令碼使用指令碼流。一旦下載開始,它允許 asyncdefer scripts 在一個單獨的後臺執行緒進行解析,因此在某些情況下,提高 10% 的頁面載入時間。實際上,在 <head>使用 <script defer>,以便 瀏覽器更早地可以發現資源,然後在後臺執行緒中解析它。

CaveatOpera Mini 不支援 defement 指令碼,如果你正在印度和非洲從事開發工作,defer 將會被忽略,導致阻塞渲染直到指令碼載入(感謝 Jeremy)!

漸進引導

漸進引導:使用伺服器端呈現獲得首次有效繪製,但也包含一些最小必要的 JavaScript 來保持實時互動來接近首次有效繪製。

  1. 客戶端渲染還是服務端渲染?

在兩種場景下,我們的目標應該是建立 漸進引導:使用服務端呈現獲得首次有效繪製,而且還要包含一些最小必要的 JavaScript 來保持實時互動來接近首次有效繪製。如果 JavaScript 在首次有效繪製沒有獲取到,那麼瀏覽器可能會在解析時鎖住主執行緒,編譯和執行最新發現的 JavaScript,從而對站點或應用程式的互動性造成限制

為了避免這樣做,總是將執行函式分離成一個個,非同步任務和可能用到 requestIdleCallback 的地方。考慮 UI 的懶載入部分使用 WebPack 動態 import 支援,避免載入、解析和編譯開銷直到使用者真的需要他們(感謝 Addy!)。

在本質上,互動時間(TTI)告訴我們導航和互動之間的時間。度量是通過在視窗初始內容呈現後的第一個五秒來定義的,在這個過程中,JavaScript 任務都不超過 50ms。如果發生超過 50ms 的任務,則重新開始搜尋五秒鐘的視窗。因此,瀏覽器首先會假定它達到了互動式,只是切換到凍結狀態,最終切換回互動式。

一旦我們達到互動式,然後,我們可以按需或等到時間允許,啟動應用程式的非必需部分。不幸的是,隨著 Paul Lewis 提到的,框架通常沒有優先順序的概念,因此漸進式引導很難用大多數庫和框架實現。如果你有時間和資源,使用該策略可以極大地改善前端效能。

  1. 你是否限制第三方指令碼的影響?

隨著所有效能優化的到位,我們常常無法控制來自業務需求的第三方指令碼。第三方指令碼的度量不受使用者體驗的影響,所以,一個單一的指令碼常常會以呼叫令人討厭的,長長的第三方指令碼為結尾,因此,破壞了為效能專門作出的努力。為了控制和減輕這些指令碼帶來的效能損失,僅非同步載入(可能通過 defer)和通過資源提示,如:dns-prefetch 或者 preconnect 加速他們是不足夠的。

正如 Yoav Weiss 在他的必須關注第三方指令碼的通訊中解釋的,在很多情況下,下載資源的這些指令碼是動態的。頁面負載之間的資源是變化的,因此我們不知道主機是從哪下載的資源以及這些資源是什麼。

這時,我們有什麼選擇?考慮 通過一個超時來使用 service workers 下載資源,如果在特定的時間間隔內資源沒有響應,返回一個空的響應告知瀏覽器執行解析頁面。你可以記錄或者限制那些失敗的第三方請求和沒有執行特定標準請求。

另一個選擇是建立一個 內容安全策略(CSP) 來限制第三方指令碼的影響,比如:不允許下載音訊和視訊。最好的選擇是通過 <iframe> 嵌入指令碼使得指令碼執行在 iframe 環境中,因此如果沒有接入頁面 DOM 的許可權,在你的域下不能執行任何程式碼。Iframe 可以 使用 sandbox 屬性進一步限制,因此你可以禁止 iframe 的任何功能,比如阻止指令碼執行,阻止警告、表單提交、外掛、訪問頂部導航等等。

例如,它可能必須要允許指令碼執行 <iframe sandbox="allow-scripts">。每一個限制都可以通過多種允許值在 'sandbox' 屬性中(幾乎處處支援)解除,所以將它們限制在允許做的最低限度。考慮使用 Safeframe 和 Intersection Observer;這將使廣告嵌入 iframe 的同時仍然排程事件或需要從 DOM 獲取資訊(例如廣告知名度)。注意新的策略如 特徵策略)、資源的大小限制、CPU 和頻寬優先順序限制損害的網路功能和會減慢瀏覽器的指令碼,例如:同步指令碼,同步 XHR 請求,document.write 和超時的實現。

要對第三方進行 壓力測試,在 DevTools 上自底向上概要地檢查頁面的效能,測試在請求被阻止或超時後會發生什麼情況,對於後者,你可以使用 WebPageTest 的 Blackhole 伺服器 72.66.115.13,你可以在你的 hosts 檔案中指定特定的域名。最好是最好是自主主機並使用單個主機名,但是同時生成一個請求對映,當指令碼變化時,暴露給第四方呼叫和檢測。

請求塊

Harry Roberts

  1. HTTP cache 頭部設定是否合理?

再次檢查一遍 expirescache-controlmax-age 和其他 HTTP cache 頭部都是否設定正確。通常,資源應該是可快取的,不管是短時間的(它們是否很可能改變),還是無限期的(它們是否是靜態的)。 你可以在需要更新的時候,改變它們 URL 中的版本即可。在任何資源上禁止頭部 Last-Modified 都會導致一個 If-Modified-Since 條件查詢,即使資源在快取中。與 Etag 一樣,即使它在使用中。

使用 Cache-control: immutable,(其實是為了解決fingerprinted靜態資源的快取問題而被設計出來的,解決了客戶端revalidation問題(截至 2017年12月,在 FireFox,Edge 和 Safari 中支援;只有 FireFox 在 HTTPS 中支援)。你也可以使用 Heroku 的 HTTP 快取頭部,Jake Archibald 的 "Caching Best Practices" ,以及 Ilya Grigorik 的 HTTP caching primer 作為指導。而且,注意不同的頭部,尤其是在關係到 CDN 時,並且注意並且要注意關鍵標頭檔案,有助於避免在新請求稍有差異時進行額外的驗證,但從以前請求標準,並不是必要的(感謝,Guy!)。

靜態資源優化

  1. 你是否使用 Brotli 或 Zopfli 進行純文字壓縮?

在 2005 年,Google 推出了 Brotli,一個新的開源無損資料壓縮格式,現在已經 被所有的現代瀏覽器所支援。實際上,Brotli 比 Gzip 和 Deflate 更有效。壓縮速度可能會非常慢,取決於設定資訊。但是緩慢的壓縮過程會提高壓縮率,並且仍然可以快速解壓。當然,解壓縮速度很快。

只有當使用者通過 HTTPS 訪問網站時,瀏覽器才會採用。Brotli 現在還不能預裝在某些伺服器上,而且如果不自己構建 NGINX 和 UBUNTU 的話很難部署。不過這也並不難。實際上,一些 CDN 是支援的,甚至 也可以通過伺服器在不支援 CDN 的情況下啟用 Brotli

在最高階別的壓縮下,Brotli 的速度會變得非常慢,以至於伺服器在等待動態壓縮資源時開始傳送響應所花費的時間可能會使我們對檔案大小的優化無效。但是,對於靜態壓縮,高壓縮比的設定比較受歡迎 —— (感謝 Jeremy!

或者,你可以考慮使用 Zopfli 的壓縮演算法,將資料編碼為 Deflate,Gzip 和 Zlib 格式。Zopfli 改進的 Deflate 編碼使得任何使用 Gzip 壓縮的檔案受益,因為這些檔案大小比 用Zlib 最強壓縮後還要小 3% 到 8%。問題在於壓縮檔案的時間是原來的大約 80倍。這就是為什麼雖然 使用 Zopfli 是一個好主意但是變化並不大,檔案都需要設計為只壓縮一次可以多次下載的。

比較好的方法是你可以繞過動態壓縮靜態資源的成本。Brotli 和 Zopfli 都可以用於明文傳輸 —— HTML,CSS,SVG,JavaScript 等。

有什麼方法呢?在最高等級和 Brotli 的 1-4 級動態壓縮 HTML 使用 Brotli+Gzip 預壓縮靜態資源。同時,檢查 Brotli 是否支援 CDN,(例如 KeyCDN,CDN77,Fastly)。確保伺服器能夠使用 Brotli 或 gzip 處理內容。如果你不能安裝或者維護伺服器上的 Brotli,那麼請使用 Zopfli。

  1. 影象是否進行了適當的優化?

儘可能通過 srcsetsizes<picture> 元素使用 響應式圖片。也可以通過 <picture> 元素使用 WebP 格式的影象(Chrom,Opera,Firefox soon支援),或者一個 JPEG 的回撥(見 Andreas Bovens 的 code snippet)或者通過使用內容協商(使用 Accept 頭資訊)。

Sketch 本身就支援 WebP 並且 WebP 影象可以通過使用 WebP 外掛 從 PhotoShop 中匯出。也有其他選擇可以使用,如果你使用 WordPress 或者 Joomla,也有可以輕鬆支援 WebP 的擴充套件,例如 OptimusCache Enabler(通過 Cody Arsenault

你可以仍然使用 client hints,但仍需要獲得一些瀏覽器支援。沒有足夠的資源支援響應式圖片?使用 斷點發生器 或者類似 Cloudinary 這樣的服務自動優化圖片。同樣,在許多情況下,只使用 srcsetsizes 會有不錯的效果。

響應影象斷點發生器

響應影象斷點生成器自動生成影象和標記生成。

  1. 圖片優化進階別

現在有一個至關重要著陸頁,有一個特定的圖片的載入速度非常關鍵,確保 JPEGs 是漸進式的並且使用 AdeptmozJPEG (通過操縱掃描級來改善開始渲染時間)或者 Guetzli 壓縮,谷歌新的開源編碼器重點是能夠感官的效能,並借鑑 Zopfli 和 WebP。唯一的 不足 是:處理的時間慢(每百萬畫素 CPU 一分鐘)。至於 png,我們可以使用 Pingo,和 svgo,對於 SVG 的處理,我們使用 SVGOSVGOMG

每一個影象優化的文章會說明,但始終會提醒要保持向量資源乾淨和緊密。確保清理未使用的資源,刪除不必要的後設資料,並減少圖稿中的路徑點數量(從而減少SVG程式碼)。(感謝,Jeremy!

到目前為止,這些優化只涵蓋了基礎知識。 Addy Osmani 已經發布了 一個非常詳細的基本影象優化指南,深入到影象壓縮和顏色管理的細節。 例如,您可以模糊影象中不必要的部分(通過對其應用高斯模糊濾鏡)以減小檔案大小,最終甚至可以開始移除顏色或將影象變成黑白色,以進一步縮小影象尺寸。 對於背景影象, 從Photoshop 匯出的照片質量為 0 到 10% 也是絕對可以接受的。

那麼 GIF 圖片呢?我們可以使用 迴圈的 HTML5 視訊,而不是影響渲染效能和頻寬的重度 GIF 動畫,而使用迴圈的 HTML5 視訊,雖然 <video> 會使得 瀏覽器的效能很慢,但是與影象不同的是,瀏覽器不會預先載入 <video> 內容。我們也可以使用 Lossy GIF, gifsicle 或者 giflossy 新增有失真壓縮 GIF。

訊息: 希望不久以後我們可以使用 <img src=".mp4"> 來載入視訊, 早期的測試表明 img 標籤比同等大小的 GIF 顯示速度的要快 20 多倍,解析速度要快 7 倍多

還不夠好?那麼,你也可以使用 多個背景影象技術 提高影象的感知效能。 記著,減少對比度 和模糊不必要的細節(或消除顏色)也可以減小檔案的大小。 你需要放大一個小照片而不失真?考慮使用 Letsenhance.io

Zach Leatherman 的字型載入策略綜合指南

Zach Leatherman 的 字型載入策略綜合指南 提供了十幾種更好的網頁字型傳送選項

  1. 是否對 Web字型進行了優化?

首先需要問一個問題,你是否能不使用 UI 系統字型。 如果不可以,那麼你有很大可能使用 Web 網路字型,會包含字形和額外的功能以及用不到的加粗。你可以向字型設計公司獲取網路字型子集或子集,如果您使用的是開源字型(例如,通過僅包含帶有某些特殊的重音字形的拉丁語),則可以只選擇部分 Web 字型來減少其檔案大小。

WOFF2 的支援非常好,對於不支援WOFF2的瀏覽器,你可以使用 WOFF 和 OTF 作為不支援它的瀏覽器的備選。另外,從 Zach Leatherman 的《字型載入策略綜合指南》(程式碼片段也可以作為 Web字型載入片段)中選擇一種策略,並使用伺服器快取持久地快取字型。是不是感覺小有成就?Pixel Ambacht 有一個 快速教程和案例研究,讓你的字型按順序排列。

如果你無法從你的伺服器拿到字型並依賴於第三方主機,請確保使用 字型載入事件(或對不支援它的瀏覽器使用 Web字型載入器FOUT 要優於 FOIT; 立即開始渲染文字,並非同步載入字型 —— 也可以使用 loadCSS。 你也可以 擺脫本地安裝的作業系統字型,也可以使用 可變的 字型

怎麼才能是一個無漏洞的字型載入策略? 從 font-display 開始,然後到 Font Loading API,然後到 Bram Stein 的 Font Face Observer感謝 Jeremy!)如果你有興趣從使用者的角度來衡量字型載入的效能, Andreas Marschke 探索了 使用 Font API 和 UserTiming API 進行效能跟蹤

此外,不要忘記包含 font-display:optional 描述符來提供彈性和快速的字型回退,unicode-range 將大字型分解成更小的語言特定的字型,以及Monica Dinculescu 的字型樣式匹配器 用來解決由於兩種字型之間的大小差異,最大限度地減少了佈局上的震動的問題。

交付優化

  1. 你是否非同步載入 JavaScript?

當使用者請求頁面時,瀏覽器獲取 HTML 並構造 DOM,然後獲取 CSS 並構造 CSSOM,然後通過匹配 DOM 和 CSSOM 生成一個渲染樹。如果有任何的 JavaScript 需要解決,瀏覽器將不會開始渲染頁面,直到 JavaScript 解決完畢,這樣就會延遲渲染。 作為開發人員,我們必須明確告訴瀏覽器不要等待並立即開始渲染頁面。 為指令碼執行此操作的方法是使用 HTML 中的 deferasync 屬性。

事實證明,我們 應該把 async 改為 defer(因為 ie9 及以下不支援 async)。 另外,如上所述,限制第三方庫和指令碼的影響,特別是使用社交共享按鈕和嵌入的 <iframe> 嵌入(如地圖)。 大小限制 有助於防止 JavaScript 庫過大:如果您不小心新增了大量依賴項,該工具將通知你並丟擲錯誤。 你可以使用 靜態社交分享按鈕(如通過 SSBG )和 靜態連結 來代替互動式地圖。

  1. 你對開銷很大的 JS 是否使用懶載入並使用 Intersection Observer?

如果您需要延遲載入圖片、視訊、廣告指令碼、A/B 測試指令碼或任何其他資源,則可以使用 Intersection Observer API,它提供了一種方法非同步觀察目標元素與 祖先元素或頂層文件的視口。基本上,你需要建立一個新的 IntersectionObserver 物件,它接收一個回撥函式和一組選項。 然後我們新增一個目標來觀察。

當目標變得可見或不可見時執行回撥函式,所以當它攔截視口時,可以在元素變得可見之前開始採取一些行動。 事實上,我們可以精確地控制觀察者的回撥何時被呼叫,使用 rootMarginthreshold(一個數字或者一個數字陣列來表示目標可見度的百分比)。Alejandro Garcia Anglada 發表了一個 簡單的教程 關於如何實際實施的教程。

你甚至可以通過向你的網頁新增 漸進式圖片載入 來將其提升到新的水平。 與 Facebook,Pinterest 和 Medium 類似,你可以首先載入低質量或模糊的影象,然後在頁面繼續載入時,使用 Guy Podjarny 提出的 LQIP (Low Quality Image Placeholders) technique(低質量影象佔位符)技術替換它們的清晰版本。(可以參考知乎)

如果技術提高了使用者體驗,觀點就不一樣了,但它肯定會提高第一次有意義的繪畫的時間。我們甚至可以通過使用 SQIP 建立影象的低質量版本作為 SVG 佔位符來實現自動化。 這些佔位符可以嵌入 HTML 中,因為它們自然可以用文字壓縮方法壓縮。 Dean Hume 在他的文章中 描述了 如何使用 Intersection Observer 來實現這種技術。

瀏覽器支援程度如何呢?Decent,與 Chrome,火狐,Edge 和 Samsung Internet 已經支援了。 WebKit 目前 正在開發中。如果瀏覽器不支援呢? 如果不支援Intersection Observer,我們仍然可以 延遲載入 一個 polyfill 或立即載入影象。甚至還有一個 library

通常,我們會使用懶載入來處理所有代價較大的元件,如字型,JavaScript,輪播,視訊和 iframe。 你甚至可以根據網路質量調整內容服務。網路資訊 API,特別是 navigator.connection.effectiveType(Chrome 62+)使用 RTT 和下行鏈路值來更準確地表示連線和使用者可以處理的資料。 您可以使用它來完全刪除視訊自動播放,背景圖片或 Web 字型,以便連線速度太慢。

  1. 你是否優先載入關鍵的 CSS?

為確保瀏覽器儘快開始渲染頁面,通常 會收集開始渲染頁面的第一個可見部分所需的所有 CSS(稱為 “關鍵CSS” 或 “首屏 CSS”)並將其內聯新增到頁面的 <head> 中,從而減少往返。 由於在慢啟動階段交換包的大小有限,所以關鍵 CSS 的預算大約是 14 KB。

如果超出這個範圍,瀏覽器將需要額外往返取得更多樣式。 CriticalCSSCritical 可以做到這一點。 你可能需要為你使用的每個模板執行此操作。 如果可能的話,考慮使用 Filament Group 使用的 條件內聯方法

使用 HTTP/2,關鍵 CSS 可以儲存在一個單獨的 CSS 檔案中,並通過 伺服器推送 來傳遞,而不會增大 HTML 的大小。 問題在於,伺服器推送是很 麻煩,因為瀏覽器中存在許多問題和競爭條件。 它一直不被支援,並有一些快取問題(參見 [Hooman Beheshti介紹的文章](Hooman Beheshti's presentation) 114 頁內容)。事實上,這種影響可能是 負面的,會使網路緩衝區膨脹,從而阻止文件中的真實幀被傳送。 而且,由於 TCP 啟動緩慢,似乎伺服器推送在熱連線上 更加有效

即使使用 HTTP/1,將關鍵 CSS 放在根目錄上的單獨檔案中也是有 好處的,有時甚至比快取和內聯更為有效。 Chrome 請求這個頁面的時候會再傳送一個 HTTP 連線到根目錄,從而不需要 TCP 連線來獲取這個 CSS(感謝 Philip!

需要注意的一點是:和 preload 不同的是,preload 可以觸發來自任何域的預載入,而你只能從你自己的域或你所授權的域中推送資源。 一旦伺服器得到來自客戶端的第一個請求,就可以啟動它。 伺服器將資源壓入快取,並在連線終止時被刪除。 但是,由於可以在多個選項卡之間重複使用 HTTP/2 連線,所以推送的資源也可以被來自其他選項卡的請求宣告(感謝 Inian!)。

目前,伺服器並沒有一個簡單的方法得知被推送的資源 是否已經存在於使用者的快取中,因此每個使用者的訪問都會繼續推送資源。因此,您可能需要建立一個 快取監測 HTTP/2 伺服器推送機制。如果被提取,您可以嘗試從快取中獲取它們,這樣可以避免再次推送。

但請記住,新的 cache-digest 規範 無需手動建立這樣的 “快取感知” 的伺服器,基本上在 HTTP/2 中宣告的一個新的幀型別就可以表達該主機的內容。因此,它對於 CDN 也是特別有用的。

對於動態內容,當伺服器需要一些時間來生成響應時,瀏覽器無法發出任何請求,因為它不知道頁面可能引用的任何子資源。 在這種情況下,我們可以預熱連線並增加 TCP 擁塞視窗大小,以便將來的請求可以更快地完成。 而且,所有內聯配置對於伺服器推送都是較好的選擇。事實上,Inian Parameshwaran 對 HTTP/2 Push 和 HTTP Preload 進行了比較 深入的研究,內容很不錯,其中包含了您可能需要的所有細節。伺服器到底是推送還是不推送呢?你可以閱讀一下 Colin Bendell 的 Should I Push?

底線:正如 Sam Saccone 所說preload 有利於將資源的開始下載時間更接近初始請求, 而伺服器推送是一個完整的 RTT(或 更多,這取決於您的伺服器反應時間 —— 如果你有一個伺服器可以防止不必要的推送。

你使用 流響應 嗎?通過流,在初始導航請求中呈現的 HTML 可以充分利用瀏覽器的流式 HTML 解析器。

  1. 你使用流響應嗎?

streams 經常被遺忘和忽略,它提供了非同步讀取或寫入資料塊的介面,在任何給定的時間內,只有一部分資料可能在記憶體中可用。 基本上,只要第一個資料塊可用,它們就允許原始請求的頁面開始處理響應,並使用針對流進行優化的解析器逐步顯示內容。

我們可以從多個來源建立一個流。例如,您可以讓伺服器構建一個殼子來自於快取,內容來自網路的流,而不是提供一個空的 UI 外殼並讓它填充它。 正如 Jeff Posnick 指出的,如果您的 web 應用程式由 CMS 提供支援的,那麼伺服器渲染 HTML 是通過將部分模板拼接在一起來呈現的,該模型將直接轉換為使用流式響應,而模板邏輯將從伺服器複製而不是你的伺服器。Jake Archibald 的 The Year of Web Streams 文章重點介紹瞭如何構建它。對於效能的提升是非常明顯的。

流式傳輸整個 HTML 響應的一個重要優點是,在初始導航請求期間呈現的 HTML 可以充分利用瀏覽器的流式 HTML 解析器。 在頁面載入之後插入到文件中的 HTML 塊(與通過 JavaScript 填充的內容一樣常見)無法利用此優化。

瀏覽器支援程度如何呢? 詳情請看這裡 Chrome 52+、Firefox 57、Safari 和 Edge 支援此 API 並且伺服器已經支援所有的 現代瀏覽器.

  1. 你使用 Save-Data 儲存資料嗎?

特別是在新興市場工作時,你可能需要考慮優化使用者選擇節省資料的體驗。 Save-Data 客戶端提示請求頭 允許我們為成本和效能受限的使用者定製應用程式和有效載荷。 實際上,您可以將 高 DPI 影象的請求重寫為低 DPI 影象,刪除網頁字型和花哨的特效,關閉視訊自動播放,伺服器推送,甚至更改提供標記的方式。

該頭部目前僅支援 Chromium,Android 版 Chrome 或 桌面裝置上的 Data Saver 擴充套件。最後,你還可以使用 service worker 和 Network Information API 來提供基於網路型別的低/高解析度的影象。

  1. 你是否啟用了連線以加快傳輸?

使用 資源提示 來節約時間,如 dns-prefetch (在後臺執行 DNS 查詢),preconnect (告訴瀏覽器在後臺進行連線握手(DNS, TCP, TLS)),prefetch (告訴瀏覽器請求一個資源) 和 preload (預先獲取資源而不執行他們)。

最近,我們至少會使用 preconnectdns-prefetch,我們會小心使用 prefetchpreload;前者只能在你非常確定使用者後續需要什麼資源的情況下使用(類似於採購渠道)。注意,prerender 已被棄用,不再被支援。

請注意,即使使用 preconnectdns-prefetch,瀏覽器也會對它將並行查詢或連線的主機數量進行限制,因此最好是將它們根據優先順序進行排序(感謝 Philip!)。

事實上,使用資源提示可能是最簡單的提高效能的方法,它確實很有效。什麼時候該使用呢?Addy Osmani 已經做了解釋,我們應該預載入確定將在當前頁面中使用的資源。預獲取可能用於未來頁面的資源,例如使用者尚未訪問的頁面所需的 Webpack 包。

Addy 的關於 Chrome 中載入優先順序的文章展示了 Chrome 是如何精確地解析資源提示的,因此一旦你決定哪些資源對頁面渲染比較重要,你就可以給它們賦予比較高的優先順序。你可以在 Chrome DevTools 網路請求表格(或者 Safari Technology Preview)中啟動“priority”列來檢視你的請求的優先順序。

the priority column in DevTools

DevTools 中的 "Priority" 列。圖片來源於:Ben Schwarz,重要的請求

例如,由於字型通常是頁面上的重要資源,所以最好使用 preload 請求瀏覽器下載字型。你也可以 動態載入 JavaScript ,從而有效的執行延遲載入。同樣的,因為 <link rel="preload"> 接收一個 media 的屬性,你可以基於 @media 查詢規則來有選擇性地優先載入資源。

需要注意的一些問題是preload 可以 將資源的下載時間移到請求開始時,但是這些快取在記憶體中的預先載入的資源是繫結在所傳送請求的頁面上,也就是說預先載入的請求不能被頁面所共享。而且,preload 與 HTTP 快取配合得也很好:如果快取命中則不會傳送網路請求。

因此,它對後發現的資源也非常有用,如:通過 background-image 載入的一幅 hero image,內聯關鍵 CSS (或 JavaScript),並預先載入其他 CSS (或 JavaScript)。此外,只有當瀏覽器從伺服器接收 HTML,並且前面的解析器找到了 preload 標籤後,preload 標籤才可以啟動預載入。由於我們不等待瀏覽器解析 HTML 以啟動請求,所以通過 HTTP 頭進行預載入要快一些。早期提示將有助於進一步,在傳送 HTML 響應標頭之前啟動預載入。

請注意:如果你正在使用 preloadas 必須定義否則什麼都不會載入,還有,預載入字型時如果沒有 crossorigin 屬性將會獲取兩次

  1. 你優化渲染效能了嗎?

使用 CSS containment 隔離昂貴的元件 - 例如,限制瀏覽器樣式、用於非畫布導航的佈局和繪畫工作,第三方元件的範圍。確保在滾動頁面沒有延遲,或者當一個元素進行動畫時,持續地達到每秒 60 幀。如果這是不可能的,那麼至少要使每秒幀數持續保持在 60 到 15 的範圍。使用 CSS 的 will-change 通知瀏覽器哪個元素的哪個屬性將要發生變化。

此外,評估執行時渲染效能(例如,使用 DevTools)。可以通過學習 Paul Lewis 免費的關於瀏覽器渲染優化的 Udacity 課程和 Emily Hayman 的文章優化網頁動畫和互動來入門。

同樣,我們有 Sergey Chikuyonok 這篇文章關於如何正確使用 GPU 動畫。注意:對 GPU-composited 層的更改是代價最小的,如果你能通過“不透明”和“變形”來觸發合成,那麼你就是在正確的道路上。

  1. 你優化過渲染體驗嗎?

元件以何種順序顯示在頁面上以及我們如何給瀏覽器提供資源固然重要,但是我們同樣也不能低估了感知效能的角色。這一概念涉及到等待的心理學,主要是讓使用者在其他事情發生時保持忙碌。這就涉及到了感知管理優先開始提前完成寬容管理

這一切意味著什麼?在載入資源時,我們可以嘗試始終領先於客戶一步,所以將很多處理放置到後臺,響應會很迅速。讓客戶參與進來,我們可以用骨架螢幕例項演示),而不是當沒有更多優化可做時、用載入指示,新增一些動畫/過渡欺騙使用者體驗

HTTP/2

  1. 遷移到 HTTPS,然後開啟 HTTP/2.

在谷歌提出向更安全的網頁進軍以及Chrome 會把(所有使用 HTTP 的)網頁認定為“不安全”的大環境下,遷移到HTTP/2是不可避免的。HTTP/2 從目前來看支援得非常好,並且,在某些場景下,使用 HTTP/2 會讓你大力出奇跡。一旦執行在 HTTPS 上,你至少能夠在 service workers 和 server push 方面獲得顯著的效能提升

HTTP/2

最終,谷歌計劃將所有 HTTP 頁面標記為不安全的,並將有問題的 HTTPS 的 HTTP 安全指示器更改為紅色三角形。(圖片來源

最耗時的任務將是遷移到 HTTPS,取決於你的 HTTP/1.1 使用者基礎有多大(即使用舊版作業系統或瀏覽器的使用者),你將不得不為舊版的瀏覽器效能優化傳送不同的構建版本,這需要你採用不同的構建流程。注意:開始遷移和新的構建過程可能會很棘手,而且耗費時間。接下來所講的內容,都是針對之前切過 HTTP/2 環境或者現在正準備切 HTTP/2 環境(的讀者)來展開的。

  1. 正確地部署 HTTP/2.

再次強調一下,需要對現階段正如何提供在你開始使用 HTTP/2 請求資源之前,需要搞清楚你以前是如何請求資源的。另外需要你在載入大模組以及並行載入小模組之間找到一個平衡點。 。最終,仍然是最好的請求就是沒有請求,然而我們的目標是在快速傳輸資源和快取之間找到一個好的平衡點。

一方面,你可能想要避免合併所有資源,而是把整個介面分解成許多小模組,然後在構建過程中壓縮這些小模組,最後通過 “scount” approach 的方法引用和並行載入這些小模組。這樣的話,一個檔案的更改就不需要重新下載整個樣式表或 JavaScript。這樣還可以 最小化解析時間 ,並將單個頁面的負荷保持在較低的水平。

另一方面,打包仍然很重要。首先,壓縮將獲益。在壓縮大檔案的過程中,藉助目錄重用的特點,達到優化效能的目的,而小的單獨的檔案則不會。有標準的工作來解決這個問題,但現在還遠遠不夠。其次,瀏覽器還沒有為這種工作流優化。例如,Chrome 將觸發程式間通訊(IPCs),與資源的數量成線性關係,因此頁面中如果包含數以百計的資源將會造成瀏覽器效能損失。

Progressive CSS loading

為了獲得使用 HTTP/2 最好的效果,可以考慮使用漸進地載入 CSS,正如 Chrome 的 Jake Archibald 所推薦的。

你可以嘗試漸進地載入 CSS。顯然,通過這樣做,這種做法不利於 HTTP/1.1 的使用者,因此在部署 HTTP/2 的過程中,你可能需要針對不同的瀏覽器建立併傳送該瀏覽器支援的 HTTP 協議的報頭,這還只是部署過程中稍微複雜的地方。你可以使用 HTTP/2 的 connection coalescing,可以讓你在 HTTP/2 環境使用域名共享)來避開這些,但在實踐中實現這一目標是很困難的。

怎麼做呢?如果你執行在 HTTP/2 之上,傳送 6-10 個包是個理想的折中(對舊版瀏覽器也不會太差)。對於你自己的網站,你可以通過實驗和測量來找到最佳的折中。

  1. 你的服務和 CDNs 支援 HTTP/2 嗎?

不同的服務和 CDNs 可能對 HTTP/2 的支援情況不一樣,但是可以使用TLS 來檢視你的可選服務,或者快速的檢視你的服務的效能以及你想要其支援的特性。 TLS工具功能如下:

Is TLS Fast Yet?

Is TLS Fast Yet? 允許你檢查你轉換到 HTTP /2 時你的伺服器和 CDN 的選項。

  1. 是否啟動了 OCSP stapling?

通過在你的服務上啟動 OCSP stapling,你可以加速 TLS 握手。線上證照狀態協議(OCSP)的提出是為了替代證照登出列表(CRL)協議。兩個協議都是用於檢查一個 SSL 證照是否已被撤回。但是,OCSP 協議不需要瀏覽器花時間下載然後在列表中搜尋認證資訊,因此減少了握手時間。

  1. 你是否已採用了 IPv6?

因為 IPv4 即將用完以及主要的行動網路正在迅速採用 IPv6(美國已經達到50% 的 IPv6 使用閾值),將你的 DNS 更新到 IPv6 以應對未來是一個好的想法。只要確保在網路上提供雙棧支援,就可以讓 IPv6 和 IPv4 同時執行。畢竟,IPv6 不是向後相容的。研究顯示,也是正因為 IPv6 自帶 NDP 以及路由優化,所以才能夠讓網站的載入速度提升10%到15%。

  1. 你使用(針對 HTTP 響應頭壓縮的)HPACK 壓縮演算法了嗎?

如果你使用 HTTP/2,請再次檢查,確保您的服務針對 HTTP 響應頭部實現 HPACK 壓縮以減少不必要的開銷。由於 HTTP/2 服務相對較新,它們可能不完全支援該規範,HPACK 就是一個例子。可以使用 H2spec 這個偉大的(如果技術上很詳細)工具來檢查。HPACK作品

h2spec

H2spec (View large version) (Image source)

H2spec (超大圖) (圖片來源)

  1. 務必保證伺服器的安全性

所有實現了 HTTP/2 的瀏覽器都在 TLS 上執行的大背景下,如果你遇到以下問題:

  • 瀏覽器中出現安全警告
  • 頁面上的某些元素不起作用。

那麼,你需要

  1. 是否使用了 service workers 來快取以及用作網路回退?

沒有什麼網路效能優化能快過使用者機器上的本地快取。如果你的網站執行在 HTTPS 上,那麼請參考使用 “Service Workers 的實用指南”。使用 service worker 中快取靜態資源並儲存離線資源(甚至離線頁面)的目的,而且還會教你如何從使用者的裝置裡面拿資料,也就是說,你現在是不需要通過網路的方式去請求之前的資料。同時,參考 Jake 的 Offline Cookbook 和 Udacity 免費課程“離線 Web 應用程式”。瀏覽器支援?如上所述,它得到了廣泛支援 (Chrome、Firefox、Safari TP、Samsung Internet、Edge 17+),但不管怎麼說,它都是網路。它有助於提高效能嗎?是的,Service Workers確實會提升效能

測試和監控

  1. 你是瞭解否在代理瀏覽器和舊版瀏覽器中測試過?

在 Chrome 和 Firefox 中進行測試是不夠的。你應該去了解你的網站在代理瀏覽器和舊版瀏覽器中是如何工作的。例如,UC 瀏覽器和 Opera Mini,這些瀏覽器 在亞洲有大量的市場份額 (達到 35%)。在你感興趣的國家測量平均網路速度從而避免在未來發現“大驚喜”。測試網路節流,並模擬一個高 DPI 裝置。BrowserStack 很不錯,但也要在實際裝置上測試。

2018 前端效能優化清單

k6 可以讓你像寫單元測試一樣編寫效能測試用例。

  1. 是否啟用了持續監控?

有一個WebPagetest私人的例項總是有利於快速和無限的測試。但是,一個帶有自動警報的連續監視工具將會給您提供更詳細的效能描述。設定您自己的使用者計時標記來度量和監視特定的業務度量。同時,考慮新增自動化效能迴歸警報來監控隨著時間而發生的變化。

使用 RUM 解決方案來監視效能隨時間的變化。對於自動化的類單元測試的負載測試工具,您可以使用 k6 指令碼 API。此外,可以瞭解下 SpeedTrackerLighthouseCalibre

速效方案

這個列表非常全面,完成所有的優化可能需要很長時間。所以,如果你只有一個小時的時間來進行重大的改進,你會怎麼做?讓我們把這一切歸結為10個低掛的水果。顯然,在你開始之前和完成之後,測量結果,包括開始渲染時間以及在 3G 和電纜連線下的速度指數。

  1. 測量實際環境的體驗並設定適當的目標。一個好的目標是:第一次有意義的繪製 < 1 s,速度指數 < 1250,在慢速的 3G 網路上的互動 < 5s,對於重複訪問,TTI < 2s。優化渲染開始時間和互動時間。

  2. 為您的主模板準備關鍵的 CSS,並將其包含在頁面的 <head> 中。(你的預算是 14 KB)。對於 CSS/JS,檔案大小不超過 170 KB gzipped(解壓後 0.8-1 MB)。

  3. 延遲載入儘可能多的指令碼,包括您自己的和第三方的指令碼——特別是社交媒體按鈕、視訊播放器和耗時的 JavaScript 指令碼。

  4. 新增資源提示,使用 dns-lookuppreconnectprefetchpreload 加速傳輸。

  5. 分離 web 字型,並以非同步方式載入它們(或切換到系統字型)。

  6. 優化影象,並在重要頁面(例如登入頁面)中考慮使用 WebP。

  7. 檢查 HTTP 快取頭和安全頭是否設定正確。

  8. 在伺服器上啟用 Brotli 或 Zopfli 壓縮。(如果做不到,不要忘記啟用 Gzip 壓縮。)

  9. 如果 HTTP/2 可用,啟用 HPACK 壓縮並開啟混合內容警告監控。如果您正在執行 LTS,也可以啟用 OCSP stapling。

  10. 在 service worker 快取中儘可能多的快取資產,如字型、樣式、JavaScript 和影象。

清單下載(PDF, Apple Pages)

記住了這個清單,您就已經為任何型別的前端效能專案做好了準備。請隨意下載該清單的列印版PDF,以及一個可編輯的蘋果頁面文件,以定製您需要的清單:

如果你需要其他選擇,你也可以參考 Rublic 的前端清單和 Jon Yablonski 的“設計師的 Web 效能清單”。

動身吧

一些優化可能超出了您的工作或預算範圍,或者由於需要處理遺留程式碼而顯得過度濫用。沒問題!使用這個清單作為一個通用(並且希望是全面的)指南,並建立適用於你的環境的你自己的問題清單。但最重要的是,測試和權衡您自己的專案,以在優化前確定問題。祝大家 2018 年的效能大漲!

非常感謝 Guy Podjarny, Yoav Weiss, Addy Osmani, Artem Denysov, Denys Mishunov, Ilya Pukhalski, Jeremy Wagner, Colin Bendell, Mark Zeman, Patrick Meenan, Leonardo Losoviz, Andy Davies, Rachel Andrew, Anselm Hannemann, Patrick Hamann, Andy Davies, Tim Kadlec, Rey Bango, Matthias Ott, Mariana Peralta, Philipp Tellis, Ryan Townsend, Mohamed Hussain S H, Jacob Groß, Tim Swalling, Bob Visser, Kev Adamson, Aleksey Kulikov and Rodney Rehm 對這篇文章的校對,同樣也感謝我們出色的社群,分享了他們在效能優化工作中學習到的技術和經驗,供大家使用。你們真正的非常了不起!

相關文章