原文地址: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
和 <
元素實現的響應式圖片。使用 WebP 格式的圖片;這可通過
picture><
標籤配合 JPEG fallback,或者通過
picutre>Accept
請求頭來實現。對於核心圖片,使用漸進式的 JPEG 並用高斯濾鏡模糊掉不重要的部分。
23 Web Font 是否被正確優化
您使用的 Web Font 很可能包含未真正被使用的執行和額外的特性。製作字型的子集(譯註:僅包含部分文字的字型,如 fontmin 等方案)。優先使用 WOFF2 並使用 WOFF 作為後備。立即使用後備字型顯示文字、非同步載入字型(例如,使用 loadCSS),然後再切換字型。同時也考慮本地作業系統中已經安裝了的字型。不要忘記在 CSS 中寫 font-display: optional
;如果你無法從您的伺服器載入字型,請記得使用 Font Load Events。
六、分發優化
24 快速推送核心 CSS
將所有首屏渲染所需要的 CSS 放在一起,然後方法在 <
標籤中。考慮有選擇的內聯的方法。或者,使用 HTTP/2 服務端推送;但這樣你可能需要構建一個可感知快取的 HTTP/2 服務端推送機制。
head>
25 使用 babel-preset-env 以僅轉譯 ES2015+ 特性
由於 ES2015 已被廣泛支援了,您可以考慮使用 babel-preset-env
以僅轉譯現代瀏覽器不支援的 ES2015+ 特性。然後你可以編譯兩份,一份是 ES6 ,另一份是 ES5。使用 <
使得有 ESM 支援的瀏覽器載入新檔案,剩下的老的瀏覽器可以使用
script type="module"><
來載入老的檔案。
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,請確保使用類似 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,並放在
<
標籤裡(你的預算是 14KB)。對於 CSS/JS,請保證核心檔案尺寸最大為 170kb (gzip 後的尺寸;壓縮前 0.8~1Mb)
head> - 延遲或懶載入儘可能多的指令碼,不管是你自己的還是第三方的——特別是分享按鈕、視訊播放器和其它的複雜模組。
- 增加資源提示,包括
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技術的發展!