2018 前端性能檢查表
原文地址:http://www.smashed.by/perf-checklist
作者 | Vitaly Friedman 譯者 | OpenWeb開發者 三三
眾所周知,性能十分重要。然而,我們真的知道性能瓶頸具體在哪兒嗎?是執行複雜的 JavaScript,下載緩慢的 Web 字體,巨大的圖片,還是卡頓的渲染?研究搖樹(Tree Shaking),作用域提升(Scope Hoisting),或是各種各樣的與 IntersectionObserver、Clients Hints、CSS containment、HTTP/2 和 Service Worker 一同工作的華麗的載入模式真的有價值嗎?最重要的是,我們從哪裡開始優化性能,以及我們如何建立長期的性能文化呢?
以前,性能往往只是事後的想法。通常直到項目最後的時候才會被考慮,然後被歸結為壓縮、合併、靜態資源優化或者對伺服器配置文件的一些細微調整。現在回想起來,事情似乎已經發生了很大的變化。
性能不僅僅是一個技術問題:它很重要,而且當把它引入到工作流時,設計決策必須根據其性能影響來決定。我們必須不斷的測量、監視和改進性能,而且 Web 日益複雜的情況帶來了新的挑戰,使得性能指標難以被跟蹤,因為性能指標將因設備、瀏覽器、協議、網路類型和延遲(CDN、運營商、緩存、代理、防火牆、負載平衡器和伺服器都在其中發揮作用)而有很大差異。
因此,如果我們創作一個在提高性能時必須牢記的所有事項的概述——從流程的一開始到網站的最終發布——這樣的列表將是什麼樣子?下面就是 2018 前端性能檢查表(但願不偏不倚和足夠客觀)——說明您可能需要考慮的問題,以確保您的站點響應時間快、用戶交互流暢,並且不會用盡用戶的帶寬。
下面是您可能需要考慮的前端性能問題的概述,以確保您的響應時間快速而流暢。
(譯註:原文詳細地闡述了文中所涉及的所有優化策略的原理和來龍去脈。此處僅翻譯了原文中附帶的 PDF 檢查表文件,意在提供一個快速、簡潔的性能優化清單。)
一、準備:規劃和指標
01 建立性能文化
只要團隊之間沒有協作,高性能就無法長期維持。研究用戶反饋中常見的抱怨,看看提高性能是否可以幫助緩解其中一些問題。用真實數據來建立適合自己的案例和模型。在設計過程中就開始規劃載入順序和權衡。
02 選擇正確的性能指標
並非每個指標都同等重要。研究最重要的度量標準:一般而言,它與您開始渲染最重要像素的速度以及提供輸入響應的速度有關。根據客戶的感受確定頁面載入的優先順序。可交互時間、頁面大標題元素的渲染時間、首次有效繪製時間(FMP)、速度指數(Speed Index)一般都很重要。
03 比你的競爭對手快至少 20%
收集代表您受眾的設備上的數據。在數據來源上,真實設備比模擬數據更好。選擇一台 Moto G4、中端三星設備或者 Nexus 5X 等性能良好的中端設備。或者,也可以通過在電腦上,通過設置網路限速(例如:150ms RTT,1.5Mbps 下載,0.7Mbps 上傳)和 CPU 限速(5 倍慢速)以模擬移動體驗。最後在常規 3G、4G 和 Wi-Fi 之間切換。收集數據、設置電子表格、將指標提高 20% 並設置目標(即,「性能預算」)。
04 把這張檢查表分享給你的同事
確保團隊中的每個成員都熟悉該清單。每一個決策都涉及性能問題,前端開發人員的積极參与將使您的項目受益匪淺。將你的性能預算映射到設計決策上。
二、制定現實的目標
05 100 毫秒的響應時間 + 每秒60幀
每幀動畫應在少於 16 毫秒(理想情況下為 10 毫秒)內完成,從而達到每秒 60 幀(1 秒 ÷ 60 = 16.6毫秒)。保持樂觀,明智地利用空閑時間。對於像動畫這樣的高壓點,只要能,就不要做任何其它事情。預計輸入延遲時間(Estimated Input Latency)應低於 50 毫秒。
06 速度指數(SpeedIndex)小於 1250,可交互時間(Time-To-Interactive)在 3G 上小於 5 秒
目標是在 1 秒內(在高速網路下)完成首次繪製(FMP),速度指數(SpeedIndex)低於 1250 毫秒。考慮速度基線是一台有著 3G 網路的,價格為 200 美元左右的 Android 手機(譯註:國產千元機水平),那麼可以以 400 毫秒 RTT 和 400kb/s 的傳輸速度進行網路模擬,以達成可交互時間(Time-To-Interactive)小於 5 秒,第二次打開的速度低於 2 秒。盡你所能地降低這些指標。
07 核心塊 = 15kb,關鍵文件 < 170 kb
HTML 的前 14~15kb 是最關鍵的核心塊(chunk),也是整個文件中唯一可以在第一個 RTT 內被下載的部分。要實現上述目標,請設定關鍵文件的最大尺寸「預算」。170kb gzip 後的文件(原始文件尺寸 0.8~1mb),在普通手機上可能需要 1 秒才能解析和編譯完成。
三、定義環境
08 選擇並設置你的構建工具
不要太注意所謂的「酷」。只要您能夠快速獲得結果,而且在維護構建過程上沒有問題就很好了。
09 漸進增強
首先設計和構建核心功能,然後再在此基礎上為功能強大的瀏覽器的高級功能增強效果,從而創建彈性的體驗。如果您的網站在性能差、網路差的機器上還能運行得比較快,那在性能好、網路棒的機器上只會運行得更快。
10 設定硬性的性能基準
用 JavaScript 實現交互效果的成本相當高昂。170kb 的尺寸預算已經包含了核心的 HTML / CSS / JavaScript、路由、狀態管理、工具函數、框架還有產品邏輯,因此,請徹底檢查我們選擇的框架的網路傳輸成本、解析 / 編譯時間和其運行時的時間成本。
11 聖戰止於智者:Angular, React 還是 Ember
並不是每個項目都需要框架。但是如果你的項目需要框架,那麼最好選擇使用一個支持伺服器端渲染(SSR)的框架。在使用框架之前,請確保在移動設備上以伺服器端渲染和客戶端渲染兩種模式來評估框架的啟動時間。了解您將依賴的框架的具體細節。了解 PRPL 模式和 App Shell 模型。
12 你會使用 AMP 或者 Instant Articles 嗎
(譯註:AMP 為 Google 的開源項目,意在以組件化的形式以提升移動設備對網站的訪問速度;Instant Articles 是 Facebook 的協議,意在通過渲染頁面的精簡版本以提升頁面在 Facebook App 內的打開速度。在國內,MIP 是和 AMP 類似的解決方案。)
沒有它們,你也可以獲得良好的性能。但是 AMP 提供了一個可靠的性能框架,有免費的 CDN ,而 Instant Articles 將提高你在 Facebook 上的知名度和性能。你也可以構建一個漸進式 AMP(譯註:Progressive Web AMP,PWA 和 AMP 的結合體)。
13 選擇合適的 CDN
您可以將部分內容「外包」給靜態站點生成器,然後將其推送到 CDN,並從CDN 提供靜態版本,從而避免資料庫請求(即 JAMStack)。當然,這取決於您擁有的動態數據量。仔細檢查 CDN 是否為您執行了內容壓縮和轉換、智能 HTTP/2 和邊緣端包含(ESI, edge-side includes)。
四、優化構建
14 合理安排優先順序
把你所有的靜態資源(JavaScript,圖片,字體,第三方腳本,尺寸大的模塊)列成一個表,然後把它們按優先順序分成三組:基本核心功能(老瀏覽器也能瀏覽的核心內容)、增強體驗效果(為現代瀏覽器設計的強大功能和豐富體驗)、附加功能(不一定需要並且可以惰性載入的資源,比如字體、輪播腳本、視頻播放器、分享按鈕等)。
15 使用「符合標準」技術
(譯註:「符合標準」技術(cutting-the-mustard technique)是 BBC News 開發者博客提出的,一種基於瀏覽器特性來檢測其支持程度,並以此選擇要載入哪些功能的技術。)
對老舊的瀏覽器,僅輸出核心功能代碼;對現代瀏覽器輸出增強的功能代碼。嚴格按標準載入靜態資源:直接載入核心代碼,在 DOMContentLoaded 事件中載入增強代碼,並在 load 事件中載入剩下的代碼。注意:廉價的 Android 手機雖然很符合標準,但這些手機的內存和 CPU 性能有限。因此,您可能需要使用讀取設備內存大小的 JavaScript API 來檢測設備性能,只有不支持的時候才按「符合標準」技術來。
16 減少 JavaScript 體積
由於解析 JavaScript 很耗時,所以請儘可能的減少 JavaScript 的體積。在構建 SPA 時,您可能需要用一定時間初始化應用程序之後,才能開始渲染頁面。尋找可以加快初始渲染事件的模塊和技術(在低端移動設備上,這可以輕鬆將速度提高 2-5 倍)。徹底檢查每一個 JavaScript 依賴,以找出誰在消耗初始化的寶貴時間。
17 使用微優化和漸進式啟動
使用伺服器端渲染來獲得快速的首次有效繪製時間(FMP),但也在頁面里輸出一些最小功能的 JavaScript 來保持交互時間(TTI)接近首次有效繪製時間(FMP)。然後,如果有需要或者有多餘的時間,才開始啟動應用程序的非必要部分。在載入時顯示一個骨架屏幕,而不是「載入中」動畫。
18 使用搖樹和代碼分割
使用搖樹(Tree Shaking)技術和代碼分割(Code Splitting)技術以減少代碼體積。
搖樹(Tree Shaking)技術是一種通過丟棄未使用的代碼以在構建過程清理代碼的方法。代碼分割(Code Splitting)技術將您的代碼拆分為按需載入的「chunks(塊)」。作用域提升(Scope Hoisting)技術使得鏈式的依賴能被無縫地轉換成行內函數。通過 WebPack 將上述技術用於您的代碼。使用 AOT 編譯器(譯註:例如 Closure Compiler)將一些客戶端計算移到服務端。
19 非同步載入 JavaScript
作為開發者,我們必須顯式地使用 defer
和 async
屬性來告訴瀏覽器不要等待腳本下載、開始渲染頁面。如果你不需要關注 IE 9 及以下版本的瀏覽器,那麼使用 defer
更好;否則,使用 async
更好。使用靜態的分享按鈕、靜態鏈接互動式地圖而不是使用第三方庫。
20 HTTP 緩存頭是否設置好了
重新檢查你是否正確的設置了 Expires, Cache-Control, Max-Age 等 HTTP 緩存控制響應頭。通常而言,一個資源要麼只被緩存很短的時間(比如經常修改的資源),要麼永久緩存(比如不會被更改的那種資源)。使用專為帶哈希指紋的靜態文件設計的響應頭 Cache-Control: imuutable
以避免瀏覽器重新請求文件。
五、靜態資源優化
21 是否使用了 Brotli 或 Zopfli 壓縮
Brotli 是一種新的無損壓縮格式。現在,所有的現代瀏覽器都支持它。它比 Gzip 和 Deflate 壓縮率更高,壓縮非常慢,但是解壓速度很快。使用最高壓縮比的 Brotli+Gzip 預壓縮靜態文件,並使用 1~4 級的 Brotli 實時壓縮動態內容。也順便檢查一下 CDN 是否支持 Brotli。或者,你也可以試試在不常變化的資源上使用 Zopfli —— 它將數據用 Deflate、Gzip 和 Zlib 格式壓縮,並且被設計為一次壓縮、多次下載。
22 圖片是否被正確優化
儘可能使用通過 srcset
、sizes
和 <picture>
元素實現的響應式圖片。使用 WebP 格式的圖片;這可通過 <picutre>
標籤配合 JPEG fallback,或者通過 Accept
請求頭來實現。對於核心圖片,使用漸進式的 JPEG 並用高斯濾鏡模糊掉不重要的部分。
23 Web Font 是否被正確優化
您使用的 Web Font 很可能包含未真正被使用的執行和額外的特性。製作字體的子集(譯註:僅包含部分文字的字體,如 fontmin 等方案)。優先使用 WOFF2 並使用 WOFF 作為後備。立即使用後備字體顯示文字、非同步載入字體(例如,使用 loadCSS),然後再切換字體。同時也考慮本地操作系統中已經安裝了的字體。不要忘記在 CSS 中寫 font-display: optional
;如果你無法從您的伺服器載入字體,請記得使用 Font Load Events。
六、分發優化
24 快速推送核心 CSS
將所有首屏渲染所需要的 CSS 放在一起,然後方法在 <head>
標籤中。考慮有選擇的內聯的方法。或者,使用 HTTP/2 服務端推送;但這樣你可能需要構建一個可感知緩存的 HTTP/2 服務端推送機制。
25 使用 babel-preset-env 以僅轉譯 ES2015+ 特性
由於 ES2015 已被廣泛支持了,您可以考慮使用 babel-preset-env
以僅轉譯現代瀏覽器不支持的 ES2015+ 特性。然後你可以編譯兩份,一份是 ES6 ,另一份是 ES5。使用 <script type="module">
使得有 ESM 支持的瀏覽器載入新文件,剩下的老的瀏覽器可以使用 <script nomodule>
來載入老的文件。
26 提升渲染性能
使用 CSS 包含(CSS Containment)隔離渲染十分耗時的組件。請保證在滑動頁面或者元素動畫的時候,頁面不會卡頓,而且你的頁面能持續以 60fps 的速度渲染。如果那不可能,那麼至少也要把 fps 控制在 15~60 之間。使用 CSS 的 will-change
屬性通知瀏覽器哪個元素將會變化。
27 使用 Intersection Observer 懶載入大型腳本
Intersection Observer API 提供了非同步監聽目標元素與祖先元素或頂層文檔視口交點中的更改的能力。瀏覽器支持?Chrome, Firefox, Edge 和三星瀏覽器都支持了。WebKit 還在開發。瀏覽器不支持?懶載入一個 polyfill。
28 是否優化了渲染體驗
不要低估感知性能的作用。在載入靜態文件時,盡量始終領先用戶一步,這樣在後台發生很多事情時,會感覺體驗上很快。例如,要讓用戶持續關注你的頁面,請使用骨架屏幕而不是一些載入中的動畫。
29 預熱連接以加快分發速度
使用骨架屏幕,然後懶載入所有的大型組件,比如字體、JavaScript、輪播圖、視頻和 iframe 等。使用資源提示(Resource Hints),如 dns-prefetch
、preconnect
、prefetch
和 preload
來節省時間。
七、HTTP/2
30 為 HTTP/2 做準備
HTTP/2 支持很好,而且提供了不小的性能提升。缺點是,您必須遷移到 HTTPS;根據您不支持 HTTP/2 的用戶群大小,你可能需要為 HTTP/1.1 和 HTTP/2 的用戶返回不同版本的代碼,這就要求您調整您的編譯工具。
31 正確地部署 HTTP/2
您需要在打包模塊和並行載入許多小模塊之間找到一個良好的平衡。將整個界面分解為許多小模塊;然後分組、壓縮和打包。整個網站分為大約 6 到 10 個包應該是一個不錯的折衷方案(對於傳統瀏覽器來說也不錯)。通過實驗和數據監測來為您的網站找到正確的平衡。
32 你為 Save-Data 頭節約數據流量了嗎
Save-Data 請求提示頭可以讓我們為關心流量費用和性能的用戶提供個性化的響應。例如,你可以把所有高清的圖片都改成低清的,不用 Web Font 和華麗的動效,關掉視頻自動播放和伺服器推送,甚至修改你的應用界面。
34 確保伺服器上的安全性是無懈可擊的
再次檢查安全標頭是否設置正確,消除已知漏洞,並檢查 SSL 證書。確保所有外部插件和跟蹤腳本都是通過 HTTPS 載入的、沒有 XSS,並且 HSTS 響應頭和內容安全策略(CSP)響應頭都已正確設置。
35 你的伺服器和 CDN 都支持 HTTP/2 嗎
不同的伺服器和 CDN 可能對 HTTP/2 有不同的支持。使用 Is TLS Fast Yet? 來檢查你的設置,或者直接看看你的伺服器性能如何,支持的特性情況怎麼樣。
36 是否啟用了 OCSP Stapling
在伺服器上啟用 OCSP Stapling 有助於提升 TLS 握手速度。OCSP 協議可以讓瀏覽器無需下載並檢索證書信息,從而減少握手時間。
37 你使用 IPv6 了嗎
研究標明,IPv6 的鄰居發現(NDP)和路由優化可以使網站快 10% ~ 15%。升級到支持 IPv6 的 DNS 以為未來做好準備。只需確保雙棧網路能正常工作——這使得 IPv6 和 IPv4 能同時運行。畢竟,IPv6 不是向後兼容的。
38 HPACK 壓縮啟用了嗎
如果你使用了 HTTP/2,再次檢查你的伺服器是否實現了 HPACK 壓縮。HPACK 壓縮可以壓縮 HTTP 響應頭,以減少不必要的開支。由於 HTTP/2 伺服器現在都很新,他們可能不能完全支持包括 HPACK 壓縮在內的所有標準。H2spec 是一個非常好的用於檢測標準支持程度的工具。
39 你使用了 Service Worker 來緩存或者提供離線內容嗎
網路再怎麼優化,也不會比本地緩存更快。如果你的網站使用了 HTTPS,那麼你可以把靜態資源放在 Service Worker 的緩存中,而不用請求網路。
八、測試和監控
40 監控混合內容警告
如果您最近從 HTTP 遷移到了 HTTPS,請確保使用類似 http://Report-URI.io 之類的服務監控了嚴格的或被動的混合內容報警。你也可以用 Mixed Content Scan 來掃描你的 HTTPS 站點上是否有非 HTTPS 的混合內容。
41 使用 DevTools 的開發工作流是優化過的嗎
選擇一個調試工具,並試著點擊每一個按鈕。請確保您理解如何分析渲染性能、控制台輸出、調試 JavaScript 和編輯 CSS 樣式。
42 是否在代理瀏覽器和老式瀏覽器上測試過了
在 Chrome 和 Firefox 上測試是不夠的。請看看你的網站在代理瀏覽器和老式瀏覽器(包括 UC 瀏覽器和 Opera Mini 等。譯者註:此處的代理瀏覽器即指國內瀏覽器中常見的雲加速功能)上是什麼樣子。統計你受眾國家的網路平均速度,避免出現重大意外。使用網路節流並模擬高 DPI 設備。雖然 BrowserStack 很好,但也得在真機上測試。
43 是否設置了持續的監控
良好的性能指標是被動和主動監控工具的組合。擁有 WebPagetest 的私有實例和使用 Lighthouse 確實有利於快速測試,但也需要使用諸如 Calibre、speedscurve 等 RUM 工具建立持續的監控體系。設置您自己的用戶計時打點以監控特定的業務速度指標。
九、速戰速決
此列表相當全面,完成所有優化可能需要相當長的時間。如果你只有一個小時的時間,但又想獲得顯著的提升,你應該怎麼做?我們挑出了 10 個最容易實現的方法。顯然,在開始之前和完成之後,請統計結果,包括 3G 和有線連接上的開始渲染時間和速度指數(SpeedIndex)。
- 統計真實的用戶體驗,設置可接受的目標。一個好的目標大致是:FMP < 1s,速度指數 < 1250,TTI 在 3G 網路上 < 5s 、二次訪問 < 2s。優化開始渲染時間和 TTI。
- 為你的主模板準備核心 CSS,並放在
<head>
標籤里(你的預算是 14KB)。對於 CSS/JS,請保證核心文件尺寸最大為 170kb (gzip 後的尺寸;壓縮前 0.8~1Mb) - 延遲或懶載入儘可能多的腳本,不管是你自己的還是第三方的——特別是分享按鈕、視頻播放器和其它的複雜模塊。
- 增加資源提示,包括
dns-lookup
,preconnect
,prefetch
和preload
。 - 為 Web Font 創建子集,並非同步載入(或者乾脆別用)。
- 優化圖片。考慮在關鍵的頁面(比如落地頁)上用 WebP 格式。
- 檢查 HTTP 緩存頭和安全頭是否正確設置了。
- 在伺服器上啟用 Brotli 或者 Zopfli 壓縮。如果不支持,別忘了開 gzip。
- 如果有 HTTP/2,啟用 HPACK 壓縮並上報混合內容警告。如果使用了 LTS,那麼請打開 OCSP 裝訂。
- 如果可能,將靜態資源(包括字體、樣式、腳本和圖片等)儘可能多地在 service worker 里緩存起來。
Huge thanks to Yoav Weiss, Addy Osmani, Artem Denysov, Denys Mishunov, Ilya Pukhalski, Jeremy Wagner, Colin Bendell, Mark Zeman, Patrick Meenan, Leonardo Losoviz, Guy Podjarny, 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 and Rodney Rehm for reviewing this article, as well as our fantastic community, which has shared techniques and lessons learned from its work in performance optimization for everybody to use. You are truly smashing!
Brilliant Open Web
BOW(Brilliant Open Web)團隊,是一個專門的Web技術建設小組,致力於推動 Open Web 技術的發展,讓Web重新成為開發者的首選。BOW 關注前端,關注Web;剖析技術、分享實踐;談談學習,也聊聊管理。關注 OpenWeb開發者(ID:BrilliantOpenWeb)公眾號,回復「加群」,讓我們一起推動 OpenWeb技術的發展!
推薦閱讀:
※TCP/IP模型
※前端日刊-2017.12.7
※《Oli-Zhao的前端一萬小時》之:離不開的Git和GitHub(2)——Git、GitHub進階(提交代碼+團隊合作)
※前端日刊-2017.12.4
※前端日刊-2017.12.2