2017 前端效能自查表

Horky發表於2016-12-26

原文連結

下面的文字在翻譯時做了些調整和補充,並不完全忠實於原文。有很多我自己也沒有深入學習的點,翻譯也不能保證準確,所以有時間還是看原文。閱讀時需要注意區分載入效能和執行時效能的不同策略。文章涵蓋很多的細節,實踐中從識別出效能瓶徑開始展開效能優化會更好。可以多瞭解下Chrome DevTools的使用。比如:
* Optimise your web development workflow視訊
* Measure Resource Loading Times 開啟後,左側還有關聯的幾篇。
* 瀏覽器渲染效能優化

通常情況下需要避免微改進,雖然它能幫助不斷優化效能,但它無法聚焦。我們需要使用可衡量的目標來貫穿整個開發流程。下面例出一些不同的模型,其中不乏片面的觀點,但早點定義出來還是必要的。

設定目標

1.超越競爭對手20%

以前的研究表明,如果讓使用者感覺到效能差異,必須提高20%以上。所以我們定義效能優化的目標也不要低於這個值。頁面的效能有幾個不同的指標:
* 頁面載入完成時間 (Full-Page loading)
* 開始渲染時間 (Start Rendering Time,使用WPT測試)
* 首次有效渲染時間 (First-meaningful paint, Chromium定義)
* 可互動時間 (Time to interactive)

首次有效渲染時間和可互動時間都可以使用Lighthouse進行測試,根據使用者情況選擇適當的測試環境,包括機型和網路環境。輸出的樣式如下:
image001
如果你願意,也可以收集這些資料,製成表格,從中去除20%再設定出你的目標(如效能預算表(Performance budgets))。這時,如果你想通過減小JS大小來改進可互動時間,就會更加有方向了。
bugdets_opt
Performance budget builder by Brad Frost
把表格同你的夥伴分享,可以讓大家都能留意到對效能的影響。

2.100ms響應時間和60fps的幀率

RAIL效能模型給出建議是:從使用者操作(input)到頁面做出反應(feedback)應當在100ms以內,這樣頁面的處理必須在50ms內完成。而對於動畫這種耗時的操作,如果控制不住時間還不如什麼都不做。保證使用者的行為可以得到及時響應,而不是因為被頁面的處理操作造成卡頓的感覺。

另外為了60fps的幀率,每幀的繪製時間應當在16ms內(最好在10ms內)完成。一些好的優化實踐或者好好利用空閒時間可以做些優化。這些是執行時效能的目標,不是載入效能。

3.首次有效繪製時間低於1.25s,SpeedIndex低於1000

理想的效能目標是,在較好的網路條件下,開始渲染時間應當低於1s,首次有效渲染時間最長也要低於1250ms,SpeedIndex低於1000。在移動端,3G網路下開始渲染時間如果在3s還是可以接受的

定義環境 (DEFINING THE ENVIRONMENT)

4.選擇你的開發工具

這裡似乎不用特別說明,大家都有自己的見解。

5.漸進增強 (Progressive Enhancement)

在架構和釋出上要保持漸進增強才能立於不敗之地。先完成核心體驗,再為不同的瀏覽器完成更為高階的功能,以此建立出彈性的使用者體驗(Resilient Web Design)。

6.Angular, React, Ember…

熟悉一個支援服務端渲染的框架。在選擇一個框架前,最好確認下服務端渲染和客戶端渲染的差異。如果發現效能問題,也很難變更所使用的框架了。對於常使用的JS框架,一定要有廣泛的瞭解,再做好充分的評估

當建立一個Web應用時,不妨瞭解下PRPL模式應用框架(Application shell)架構
PRPL
PRPL代表的是:拉取關鍵資源,渲染首屏,資源預取,按需載入。
application-shell
一個應用框架(Application-Shell)就是最簡化的HTML,CSS和JS。

7.AMP或是Instant Articles?

你或許會考慮使用Google AMP或是Facebook的Instant Articles。AMP提供了一個穩健的效能框架,而Instant Articles可以使用在Facebook上體驗上更好的效能。
當然你也可以考慮下漸進式Web AMPs

8.好好選擇CDN

無論什麼頁面,總可以其中一部分轉改為靜態資源(Static Site Generator),通過CDN部署,避免資料庫請求。
你也可以選擇基於CDN的static-hosting平臺,再通過一些互動元件來增強(JAMStack)。

CDN也是可以提供動態內容的,並不需要只限定於靜態內容。選擇CDN時可以確認下是否會做壓縮、轉換、HTTP2,組裝靜態和動態內容等內容。

優化

9.設定優先順序

先要弄清出從哪入手。可以盤點下頁面中的資源(JS,圖片,字型,第三方指令碼和其它較重的模組,如輪播(Carousel)元件, 複雜的圖文,以及多媒體資源),將它們分類列出。並區分出屬於哪類需求:
* 核心體驗 (Core experience)
* 增強體驗 (Enhanced experience)
* 額外體驗 (Extras experience, 可以進行懶載入)
可以參考這個文章中的實踐進行優化。

10.使用cutting-the-mustard技術

使用cutting-the-mustard技術在老舊瀏覽器擁用核心體驗,同時在現代瀏覽器上達到更好的使用者體驗。對應限制資源載入:
* 立即載入核心體驗所需要的資源。
* 在DomContentLoaded後載入增強體驗所需要的資源。
* 在load事件為額外體驗載入其它資源。

注意通過瀏覽器版本判斷裝置能力的方法並不準確,而且現在也沒有更好的方法。

11.考慮使用微優化(micro-optimization)和漸進式啟動(progressive booting)

一些應用在可以渲染頁面之前,或許需要一些時間來進行初始化。這時不要顯示載入提示,那個沒有任何意義。最好能顯示出一個基本的樣子(skeleton screens)出來。
應用一些為初始化時間提速的技術,如tree-shakingcode-splitting。還可以使用一個前置編譯器通過服務端降低客戶端渲染的耗時,儘快顯示有意義的內容。
最後,還可以考慮使用Optimize.js來實現快速初始化(但可能不再需要了)。
progressive-booting
漸進式啟動(Progressive booting)就是先通過伺服器端渲染快速完成首次有效渲染,再通過最小的JS讓可互動時間接近於首次有效渲染時間。

客戶端渲染還是伺服器端渲染呢?通過漸進式啟動(Progressive booting)就是先通過伺服器端渲染快速完成首次有效渲染,再通過最小的JS讓可互動時間接近於首次有效渲染時間。然後根據需要載入其它內容。不幸的是,Paul Lewis卻指出這個實踐很難通過現有的框架實現。但你仍然可以考慮用它完成啟動速度的極致優化。

12.HTTP cache的響應頭有沒有設定好

這一點很容易被忽略。一定要確認expires, cache-control, max-age等這些http cache相應的響應頭都設定正確了。

可能的話,就為靜態資源使用cache-control: immutable,避免不必要的驗證(目前還只有Firefox針對https頁面支援)。詳細的內容,可以參考:
Heroku的Primer on HTTP caching headers, Jake Archibald的Caching Best Practices, 以及Ilya Grigorik的HTTP caching primer

13.限制第三方庫的使用,非同步載入JS

因為瀏覽器渲染頁面的流程,JS資源會阻塞渲染流程。對於一些不會影響首屏渲染的JS可以使用deferasync屬性。
在實踐中,如果需要考慮IE9之前使用者,建議使用async。另外要限制第三方庫和指令碼的使用,特別是分享按鈕和<iframe>中引用的。可以使用靜態分享按鈕(比如SSBG)和靜態連結(static links to interactive maps)的方式來解決。

14.優化圖片

儘量使用響應式圖片<picture>元素。還可以利用<picutre>加上JPEG回退處理來使用WebP格式的圖片。對於UC瀏覽器也可以使用H.264格式圖片。Sketch直接支援WebP, 也可以在Photoshop中使用外掛匯出,或者其它方案
responsive-image
Responsive Image Breakpoints Generator可以自動處理圖片,並生成標籤。

也可以嘗試使用client hints,增加更多的裝置資訊,讓伺服器返回不同的圖片請求。需要注意它對CDN的影響。工具Responsive Image Breakpoints Generator或者Cloudinary也可以幫助簡化你的工作。大部分情況下,單獨使用srcsetsizes也能有很不錯的效果。
另外在Smashing Magazine, 在圖片命名時使用-opt字尾,如brotli-compression-opt.png,所有人都會知道這個圖片已經被優化了。

15.進一步優化圖片

如果頁面需要快速的載入圖片,可以:
* 使用Progressive JPEG,並且使用mozJPEG壓縮一下,它能夠通過處理掃描層次來改善啟始渲染時間。
* 使用Pingo處理PNG圖片。
* 使用Lossy GIF處理GIF圖片。
* 使用SVGOMG處理SVG圖片。
去掉圖片一些不必要的部節,比如縮圖,EXIF資訊,或者應用高斯模糊過濾下,這樣可以降低圖片大小 (那些資料有時比你想像的要大很多!)。必要時可以降色,甚至是使用黑白圖片。對於背景圖,在匯出圖片時以0~10%之間的品質輸出也是可以的。

可以參考瞭解更多的內容:
* Improving Perceived Performance with Multiple Background Images
* How Medium does progressive image loading
* Tiny Thumbnails
* The “Blur Up” Technique for Loading Background Images

16.優化字型檔案了嗎?

這個主題並不太適用於中文環境,可以做個瞭解。如果使用中文字型,需要特別慎重,它們的字型檔案都是以MB為單位的。
概括一下要點:
* 使用WOFF2。
* 參考Comprehensive Guide to Font-Loading Strategies並且使用Serivce Worker - Cache來快取字型。
* 要快速有效,可以參考Pixel Ambacht的方法
* 如果依賴於第三方提供的字型,可以使用Web Font LoaderFOUT會優於FOIT
* 非同步的字型載入可以使用loadCSS
* 使用系統字型代替
* 網頁字型優化
再給一箇中文字型的參考:
* Web 字型的選擇和運用

17.及早推送關鍵的CSS

把首屏需要的樣式,即關鍵的CSS(Critical CSS), inline到頁面裡已經是常用的實踐,但本質上它只適用於首次載入顯示的場景(Why inlining everything is not answer!)。在行動網路下,要綜合主文件的大小來決定內聯CSS的策略,因為內聯CSS增大了主文件反而可能會使得主文件的首次有效渲染的耗時變長,特別是忽略了瀏覽器本身的Memory Cache和Http Cache的作用。(所以對於任何教條式的規則一定要區分它所針對的場景!!)

綜合考慮載入的耗時,關鍵CSS的大小最好保持在14KB,注意這是PC上的資料!如果是行動網路,不要讓主文件太大,總之越小越好。有兩個工具可以幫助完成:CriticalCSSCritical
也可以使用Filament Group的conditional inlining approach

如果使用HTTP/2,關鍵CSS可以存在多個CSS檔案,通過伺服器推送到達客戶端,不過可能遇到一些快取上的坑,參考Hooman Beheshti幻燈片中第 114頁。但是伺服器推送還是比保持連線更為有效,可以考慮建立一個可感知快取狀態的伺服器推送機制。已經有一個新的cache-digest規範嘗試從標準層解決這個問題了。

18.使用Tree-shaking和Code-splitting減少載入的流量

Tree-shaking能夠幫你清理掉生產環境中不需要的程式碼,也可以使用Webpack 2去掉不需的模組輸出。使用UnCSS或者Helium清除掉不再使用的CSS。另外,你可能還會考慮下如何寫出更有效的CSS選擇器,以及如何避免引入複雜的樣式

Webpack的另一個特性, Code-Splitting, 可以將程式碼分段以便按需載入。它可以幫助你做到減少初次下載,其餘程式碼則按需載入。

還是 Rollup比Browserify的匯出有更好的表現。使用Rollupify還可以將ECMAScript 2015轉成一個大的CommonJS模組,因為小的模組會有更大的效能開銷

19.改進渲染效能

將渲染開銷較大的元件使用CSS containment隔離開來,比如限定樣式的作用域,或者第三方外掛(widget)等。確保在動畫或者滑屏時不會有卡頓。如果實在做不到,可以讓頁面交錯達到60~15的幀率。使用will-change來通知瀏覽器哪些元素的哪些屬性會發生變化。

另外,最好還是通過Chrome DevTools來全面的評估渲染效能。可以從Paul Lewis的免費課程開始。另一個不錯的文章是Sergey Chikuyonok的GPU Animation: Doing It Right

20.預熱網路連線

使用Skeleton screens(先顯示基本的框架),並且對開銷大的元素執行懶載入,如字型、JS、輪播元素、視訊和iframe。也可以使用Resource Hints中定義的一套預處理策略。
* dns-prefetch DNS預解析
* preconnect 預連線
* prefetch 預載入且會執行(JS)
* preload 預載入,但不會執行
* prerender 預渲染,成本太高不建議使用
實踐中並不是需要這些特性混合使用,比如通常preconnect會比dns-prefetch更有效。preload則會比prefetchprerender有更高的收益(更易於使用)。

翻譯就到這裡了,剩餘的部分主要和HTTP/2有關,裡面還是有些關鍵內容的,簡單概述如下:
* 有沒有使用Brotli或者Zopfli壓縮?
* 有沒有使用OCSP簡化TLS握手?
* 有沒有使用HPACK精簡響應頭?
* 有沒有使用Service Worker進行更精細的控制?

相關文章