前言:這篇文章我在掘金翻譯計劃中跟着一塊兒翻譯的文章(感謝掘金翻譯),我翻譯了第三部分,而後校對了第二部分,這篇文章對於前端性能優化的技術仍是比較新穎和全面的,因此決定本身閱讀一遍英文原文,而後又用思惟導圖整理了下重點,英文原文仍是挺長的,因此javascript
推薦你們時間充裕的話能夠本身閱讀英文原文,此文是根據掘金翻譯的四篇文章 + 其餘的翻譯文章 + 本身翻譯修改得出的。由於翻譯你們懂得,會有一些錯誤的地方,歡迎你們指出,本人不保證翻譯沒有錯誤,但已經盡力去翻譯了,因此歡迎你們直接閱讀英文原文!!歡迎你們直接閱讀英文原文!!歡迎你們直接閱讀英文原文!!php
咱們都知道性能很重要,可是,咱們是否真的一直都知道咱們性能優化的瓶頸在哪?是代價昂貴的 JavaScript?是 web 字體很慢?仍是比較大的圖片?或者是渲染速度過於遲緩?tree-shaking(無用代碼移除)、scope hoisting(做用域提高)、code-splitting(按需加載)、 intersection observer 以及 clients hints、CSS containment、HTTP/2 和 service workers 這些技術都是有利於性能優化的。而且,最重要的是,咱們要從哪裏開始提高咱們的性能呢?而且要怎麼創建長久有效的性能機制。css
譯者注:html
tree-shaking
:tree-shaking
是 Webpack 2 引入的新功能,tree-shaking
是無用代碼移除(DCE, dead code elimination)的一個方法,但和傳統的方法不太同樣。tree-shaking
找到須要的代碼,灌入最終的結果;傳統 DCE 找到執行不到的代碼,從 AST 裏清除。—— 如何評價 Webpack 2 新引入的 Tree-shaking 代碼優化技術?scope hoisting
:scope hoisting
是 Webpack 3 的新功能,又譯做「做用域提高」。Webpack 將全部模塊都用函數包裹起來,而後本身實現了一套模塊加載、執行與緩存的功能,使用這樣的結構是爲了更容易實現 Code Splitting(包括按需加載)、模塊熱替換等功能。—— Webpack 3 的新功能:Scope Hoistingcode-splitting
:對於大型的 web 應用而言,把全部的代碼放到一個文件的作法效率不好,特別是在加載了一些只有在特定環境下才會使用到的阻塞的代碼的時候。Webpack有個功能會把你的代碼分離成Chunk,後者能夠按需加載。這個功能就是code-splitting
。—— 在Webpack中使用Code Splitting實現按需加載intersection observer
:能夠自動"觀察"元素是否可見,因爲可見(visible)的本質是,目標元素與視口產生一個交叉區,因此這個 API 叫作"交叉觀察器"。—— IntersectionObserver API 使用教程clients hints
:自動響應式圖片 —— Automatic responsive images with Client HintsCSS containment
:新的 CSS 屬性 Containment 容許開發者限制瀏覽器樣式、佈局和 paint 工做的範圍。—— CSS Containment in Chrome 52service workers
:實現離線頁面 ——Service worker concepts and usage在之前,性能優化常常是在項目完成纔去考慮的,常常被推遲到項目的末期,這樣優化的範圍就會縮小,優化的內容一般是優化靜態文件和調整一些服務器配置文件。可是如今,性能優化發生了很大的改變,這些是遠遠不夠的。前端
性能不只僅是一個技術問題:重要的是,當將它放入工做流中的時候,作出的設計決策必須受性能的提示。性能必須被測量、監測和完善,而網絡日益增加的複雜性也使得跟蹤性能的度量原來越難,由於度量會根據設備、瀏覽器、協議、網絡類型和延遲而顯着變化(CDN,ISP,緩存,代理,防火牆,負載均衡器和服務器都在性能上發揮做用)。vue
因此,咱們就必須時刻記住性能優化 —— 從項目開始的時候直到網站最後發佈。因此咱們就建立了一個性能優化列表。(但願是公正的和客觀的)2018 前端性能檢查表 —— 能夠幫助你解決你網站的優化問題,例如,響應時間如何更快,流暢的用戶交互和不會流失用戶的帶寬。java
(你也能夠僅下載 2018 前端性能優化清單 的 PDF 版本(0.129 MB)或者下載 Apple Pages 版本(0.236 MB))node
微小的優化對於保持性能也是十分重要的,可是時刻記着咱們必需要有一個明確的目標,可度量的目標將影響整個過程當中的任何決策。這裏有幾種不一樣的模式,而且下面的方法都是從自身角度出發,因此你要儘早確認你本身優化的優先級。react
在不少團隊中,前端開發者確切的知道常見的潛在問題是什麼,應該用什麼樣的加載模塊來修復。然而,只要開發或者設計那邊有一方跟市場的意見不合,性能就不會長時間持續下去。因此,研究用戶常見的抱怨的地方,看看提升性能如何幫助解決這些常見問題。webpack
不管是在移動設備上仍是在桌面上都須要運行性能實驗並測量結果。 它會創建一個用真實數據爲大家公司量身定製的案例研究。此外,要善於使用 WPO Stats 上發佈的案例研究和實驗中的數據,能夠幫助提升你對性能和業務以前潛在關係的認識,以及性能對用戶體驗和業務度量的影響。僅僅說明績效是不夠的 - 你還須要創建一些可衡量和可追蹤的目標來觀察性能。
根據 心理學研究,若是你想要用戶感受你的網站比你競爭者的網站快,那麼你至少須要比它們快 20%。研究你的主要競爭者,收集他們在手機和桌面上的性能,而且設置一個你能超過他們的臨界值。要獲取準確的結果和目標,首先要研究你的分析結果,看看你的用戶都在作什麼。你能夠模擬第百分之九十的用戶經驗進行測試,收集數據,而後創建一個 表格,而後再減去 20% 就是你的目標(即 性能預算)。如今你在測試以前就有了一些可度量的數據。
若是你比較在乎預算並嘗試去用最小的腳原本得到最快的交互時間,那麼你就開始了你的「前端優化之旅」。Lara Hogan 關於如何使用性能預算來處理設計的 指南 能夠爲設計人員提供有用的指引,而 性能預算計算器 和 Browser Calories 裏均可以幫助建立預算(感謝 Karolina Szczur 提供)。
除了性能外,還要考慮對你業務最有利的客戶的行爲。 設置和討論關鍵行動的可接受時間閾值,並設置一個整個團隊已經達成一致的 「UX ready」用戶時間標記。 大部分狀況下,用戶的行爲涉及許多不一樣部門,所以,按照設置的可接受的時間安排會避免不少問題。確保額外的資源和功能的額外成本其餘成員都是可見和知曉的。
此外,正如 Patrick Meenan 所建議的那樣,最好在設計的過程當中設置一個加載隊列而且要知道這些順序會存在哪些利弊。你須要優先處理更重要的優先級,而且定義一個它們優先級的順序,你也應該清楚哪些是能夠推遲的。理想的狀況,該隊列也應該反映出 JS 和 CSS 的加載順序,只要設計好了,那麼在構建過程當中處理它們就會很簡單了。另外,還須要考慮的是在頁面還在加載時頁面的「中間狀態」(例如,當 web 字體沒有加載完時)。
計劃計劃計劃,重要的事情說三遍!若是早期進行優化那麼會很容易實現目標,可是沒有計劃或者沒有制定切合實際的、爲公司量身定製的業績目標那麼就很難保持性能。
不是全部的度量都同樣重要。 研究哪些度量對於你的應用程序最重要:一般,這與你可以以多快的速度開始渲染最重要的像素(以及它們的效果)以及如何爲這些渲染像素提供輸入最快的響應速度有關。 這能夠幫助你爲後續的工做提供最佳的優化結果。 無論怎樣,不要專一於整個頁面加載時間(例如 onLoad
和 DOMContentLoaded
時間),而是要優先按照用戶能夠感知到的頁面加載。 也就是說要關注一組稍微不一樣的度量。
首次有內容渲染,視覺完整和可交互時間之間的區別。來自於:@denar90
你能夠參考下面這樣度量:
Steve Souders 對 每一個度量都進行了詳細的解釋。在許多狀況下,根據你的應用程序的使用場景,可交互時間和輸入響應 會是最關鍵的。但這些度量可能會不一樣:例如,對於 Netflix 電視的用戶界面來講,關鍵輸入響應、內存使用和可交互時間更爲重要。
爲了收集準確的數據,咱們須要儘量全的選擇要測試的設備。最好是 Moto G4,或者一款中檔的三星設備又或者是一個 Nexus 5X 這樣的普通的設備。若是你手邊沒有設備,能夠使用節流 CPU(5× 減速)來限制網速(例如,150 ms 的往返時延,1.5 Mbps 如下和0.7 Mbps 以上)實如今桌面設備上模擬移動設備的體驗。最終,切換到常規的 3G,4G 和 Wi-Fi。爲了使性能體驗的影響更明顯,你甚至能夠使用 2G 或 一個節流的 3G 網絡,以便進行更快的測試。
幸運的是,有不少優秀的選項能夠幫助你自動收集數據,並根據這些度量衡量你的網站在一段時間內的性能。 請記住,良好的性能度量是須要被動和主動監控工具的組合:
前者在開發過程當中特別有用,由於它能夠在使用產品時持續跟蹤。 後者在長期維護很是有用,由於它能夠幫助你瞭解在實際訪問站點時發生的性能瓶頸。經過使用導航定時、資源定時、繪圖定時、長時間任務等內置的 RUM API,被動和主動性能監視工具一塊兒提供應用程序性能的完整畫面。 例如,你能夠使用PWMetrics,Calibre,SpeedCurve,mPulse,Boomerang 和 Sitespeed.io,這些都是性能監測工具的絕佳選擇。
注意:選擇瀏覽器外部的網絡級別的限制器老是比較安全的,例如 DevTools 因爲實現的方式而存在與 HTTP/2 推送交互的問題(感謝Yoav!)。
爲了不誤解,要確保你團隊裏的每一個同事都對清單很熟悉。每一個決策都對性能有影響。項目將極大地受益於前端開發人員正確地將性能價值傳達給整個團隊。從而使每一個人都對它負責,而不只僅是前端開發人員。根據績效預算和清單中定義的優先順序來設計決策。
爲了讓交互感受起來很順暢,接口有 100ms 來響應用戶的輸入。任何比它長的時間,用戶都會認爲該應用程序很慢。RAIL,一個以用戶爲中心的性能模型會爲你提供健壯的目標。爲了讓頁面達到小於 100ms 的響應,頁面必需要在小於 50ms 前最遲將控制權返回給主線程。預計輸入延遲時間 告訴咱們,若是咱們能達到這個門檻,在理想狀況下,它應該低於 50ms。對於像動畫這樣性能消耗比較大的地方,最好的作法是,在可以優化的地方,儘可能優化到極致;在不能優化的地方,讓性能開銷降至最低。
同時,每一幀動畫應該要在 16 毫秒內完成,從而達到 60 幀每秒(1秒 ÷ 60 = 16.6 毫秒) —— 最好能夠在 10 毫秒完成。由於瀏覽器須要時間將新框架繪製到屏幕上,你的代碼應該在觸發 16.6 毫秒之內完成。保持樂觀和明智地利用空閒時間。顯然,這些目標適用於運行時的性能,而不是加載性能。
雖然這可能很難實現,一個好的最終目標是首次有效渲染低於 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 。像 Calibre,SpeedCurve 和 Bundlesize 這些工具能夠幫助你保持你的預算控制,並集成到你的構建過程。
現現在不要太在乎那些很酷的技術棧。根據你的項目使用你的構建工具,不管是 Grunt,Gulp,Webpack,Parcel,仍是工具間的組合。只要你能快速的獲得結果,而且保證你的構建過程沒問題。那麼,你就能夠選擇該構建工具。
安全的選擇是將 漸進式加強 做爲前端架構和項目部署的指導原則。首先設計和構建核心體驗,而後爲有能力的瀏覽器使用高級特性加強體驗,創造 彈性 體驗。若是你的網站是在一個網絡不佳的而且有個糟糕的顯示屏上糟糕的瀏覽器上運行,速度還很快的話,那麼,當它運行在一個快速網絡下快速的瀏覽器的機器上,它只會運行得更快。
有這麼多未知因素影響加載 —— 網絡、熱保護、緩存回收、第三方腳本、解析器阻塞模式、磁盤的讀寫、IPC jank、插件安裝、CPU、硬件和內存限制、L2/L3緩存、RTTS、圖像、Web字體加載行爲的差別 —— JavaScript 的代價是最大的,web 字體阻塞默認渲染和圖片的加載消耗了大量的內存。隨着性能瓶頸從 服務器端轉移到客戶端,做爲開發人員,咱們必須更仔細地考慮全部這些未知因素。
在 170kb 的預算中,已經包括了關鍵路徑的 HTML / CSS / JavaScript、路由器、狀態管理、實用程序、框架和應用程序邏輯,咱們必須完全檢查網絡傳輸成本,解析/編譯時間和運行時間來選擇咱們的框架。
正如 Seb Markbage 所 指出的,測量框架的啓動成本的好方法是首先渲染視圖,再刪除它,而後再渲染,由於它能夠告訴你框架是如何擴展的。
第一次渲染傾向於預熱一堆編譯遲緩的代碼,當它擴展時,更大的分支能夠從中受益。第二次渲染基本上是仿效頁面上的代碼重用是如何隨着頁面複雜度的增加來影響性能特徵。
並非每一個項目都須要框架。事實上,某些項目 能夠徹底移除某些框架並從中受益。一旦選擇了一個框架,你最少會使用好幾年。因此,若是你須要使用它,確保你的選擇是通過深思熟慮的 而且 對其徹底瞭解。在進行選擇前,至少要考慮總大小的成本 + 初始解析時間:輕量級的選項像 Preact,Inferno,Vue,Svelte 或者 Polymer 都作得很好。框架的大小基線將爲你的應用程序代碼定義約束條件。。
請記住,在移動設備上,與臺式計算機相比,預計速度會有 4-5 倍的降低。由於移動設備的 GPU、CPU、內存及電池特性都不一樣。在手機上的解析時間比桌面設備的要高 36%。因此總在一個最能表明廣泛用戶的平均的設備上測試。
不一樣的框架將會對性能產生不一樣的影響,而且須要不一樣的優化策略。所以,你必須清楚地瞭解你所依賴的框架的全部細節。當建立一個 web 應用的時候,參考 PRPL 模式 和 應用程序 shell 體系結構。這個想法很簡單: 用最少的代碼來將初始路由的交互快速呈現,而後使用 service worker 進行緩存和預緩存資源,而後使用懶加載異步加載所需的路由。
PRPL 表明的是保持推送關鍵資源,渲染初始路由,預緩存剩餘路由和延遲加載必要的剩餘路由。
應用程序 shell 是最小的 HTML、CSS 和 JavaScript 驅動的用戶界面。
譯者注: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)
根據你擁有的動態數據量,你能夠將部份內容外包給靜態站點生成工具 如 Jekyll、Hexo 生成的靜態文件,接着把靜態文件推到 CDN 中,最後 CDN 只是提供靜態文件的靜態版本。因此這樣就能夠避免發起對數據庫的讀寫請求。你甚至能夠選擇一個基於 CDN 的靜態主機平臺,(這樣就能夠)經過給頁面添加可交互組件的方式來豐富你的頁面,並以此做爲性能提高的標誌 (JAMStack)。
請注意,CDN 也是能夠託管並卸載(offload)動態內容的,因此我們沒有必要把 CDN 的服務範圍限定在靜態資源。(另外須要你記住的是),無論你的 CDN 是否執行內容壓縮(GZip)、內容轉換、HTTP/2 傳輸以及 ESI(一種標記語言,能夠用它把網頁劃分爲單獨的可緩存的實體)等操做,咱們仍是須要複覈上述操做的,這是由於上述操做不只會在 CDN 的 edge 處(服務器最接近用戶的地方)聚合頁面中的靜態以及動態內容,也還會執行其它任務。
你應該知道優先處理什麼。運行你全部靜態資源(JavaScript、圖片、字體、第三方腳本和頁面中「昂貴的」模塊,好比:輪播圖、複雜的圖表和多媒體內容),並將它們劃分紅組。
先搞清楚資源(assets)能夠分爲幾類,大體能夠分爲:
咱們在「Improving Smashing Magazine's Performance」上發佈了一篇文章,上面詳細描述了該方法。
雖然這個技術已經很老了,但咱們仍然能夠使用 cutting-the-mustard 技術 使傳統瀏覽器使用核心功能並加強對現代瀏覽器的體驗。嚴格限制加載的資源:優先加載核心功能,而後是提高的,最後是其餘的。注意:該技術能夠從瀏覽器版本中推斷出設備功能,而如今咱們已經再也不這樣作了。
例如:在發展中國家,廉價的安卓手機主要運行 Chrome,他們的內存和 CPU 有限。PRPL 模式 就是一個好的選擇。最終,使用 Device Memory Client Hints Header,咱們就可以更可靠地識別出低端設備。如今,只有在 Blink 中才支持 header (Blink 支持client hints)。由於設備存儲也有一個在 Chrome 中能夠調用的 JavaScript API,一種選擇是基於 API 的特性檢測,只在不支持的狀況下回退到「符合標準」技術(謝謝,Yoav!)。
當咱們處理單頁面應用時,在你的頁面渲染以前你須要初始化應用。尋找模塊和技術加快初始化渲染時間(例如:如何調試 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-analyzer、Source Map Explorer 和 Bundle Buddy 這樣的工具能夠幫助你完成這些。度量 JavaScript 解析和編譯時間。Etsy 的 DeviceTiming,一個小工具可讓你的 JavaScript 測量任何設備或瀏覽器上的解析和執行時間。重要的是,雖然文件的大小重要,但它不是最重要的。解析和編譯時間並非隨着腳本大小增長而線性增長。
Webpack Bundle Analyzer visualizes JavaScript dependencies.
使用 ahead-of-time 編譯器 來 減輕從客戶端 到 服務端的渲染 的開銷,所以快速輸出有用的結果。最後,考慮使用 Optimize.js 經過包裝可快速調用的函數來實現(在app初始時可以)快速載入(儘管,它可能不須要)。
Tree-shaking 是一種清理構建過程的方法經過只加載生產中實際使用的代碼並清除 在 Webpack 中 未使用的 import
。使用 Webpack 3 和 Rollup,咱們還能夠使用 scope hoisting(做用域提高),scope hoisting
容許工具檢測哪些 import
能夠被提高或者能夠轉換成一個內聯函數。有了 Webpack 4,你如今能夠使用 JSON Tree Shaking。UnCSS 或者 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 的從快速默認:現代加載的最佳實踐。幻燈片76。
最後,隨着現代瀏覽器對 ES2015 支持愈來愈好,考慮 使用babel-preset-env
只轉換現代瀏覽器不支持的 ES2015+ 的特性。而後設置兩個構建,一個爲 ES6 一個爲 ES5。咱們能夠 使用script type="module"
讓具備 ES 模塊瀏覽器支持加載文件,而老的瀏覽器能夠加載傳統的 script nomodule
。
對於 loadsh,使用 babel-plugin-lodash
將只加載你僅在源碼中使用的模塊。這可能會爲你節省至關多的 JavaScript 負載。
研究 JavaScript 引擎在用戶基礎中佔的比例,而後探索優化它們的方法。例如,當優化的 V8 引擎是用在 Blink 瀏覽器,Node.js 運行和 Electron 的時候,對每一個腳本使用腳本流。一旦下載開始,它容許 async
或 defer scripts
在一個單獨的後臺線程進行解析,所以在某些狀況下,提升 10% 的頁面加載時間。實際上,在 <head>
中 使用 <script defer>
,以便 瀏覽器更早地能夠發現資源,而後在後臺線程中解析它。
Caveat:Opera Mini 不支持 defement 腳本,若是你正在印度和非洲從事開發工做,defer
將會被忽略,致使阻塞渲染直到腳本加載(感謝 Jeremy)!。
漸進引導:使用服務器端呈現得到首次有效繪製,但也包含一些最小必要的 JavaScript 來保持實時交互來接近首次有效繪製。
在兩種場景下,咱們的目標應該是創建 漸進引導:使用服務端呈現得到首次有效繪製,並且還要包含一些最小必要的 JavaScript 來保持實時交互來接近首次有效繪製。若是 JavaScript 在首次有效繪製沒有獲取到,那麼瀏覽器可能會在解析時鎖住主線程,編譯和執行最新發現的 JavaScript,從而對站點或應用程序的交互性形成限制。
爲了不這樣作,老是將執行函數分離成一個個,異步任務和可能用到 requestIdleCallback
的地方。考慮 UI 的懶加載部分使用 WebPack 動態 import
支持,避免加載、解析和編譯開銷直到用戶真的須要他們(感謝 Addy!)。
在本質上,交互時間(TTI)告訴咱們導航和交互之間的時間。度量是經過在窗口初始內容呈現後的第一個五秒來定義的,在這個過程當中,JavaScript 任務都不超過 50ms。若是發生超過 50ms 的任務,則從新開始搜索五秒鐘的窗口。所以,瀏覽器首先會假定它達到了交互式,只是切換到凍結狀態,最終切換回交互式。
一旦咱們達到交互式,而後,咱們能夠按需或等到時間容許,啓動應用程序的非必需部分。不幸的是,隨着 Paul Lewis 提到的,框架一般沒有優先級的概念,所以漸進式引導很難用大多數庫和框架實現。若是你有時間和資源,使用該策略能夠極大地改善前端性能。
隨着全部性能優化的到位,咱們經常沒法控制來自業務需求的第三方腳本。第三方腳本的度量不受用戶體驗的影響,因此,一個單一的腳本經常會以調用使人討厭的,長長的第三方腳本爲結尾,所以,破壞了爲性能專門做出的努力。爲了控制和減輕這些腳本帶來的性能損失,僅異步加載(可能經過 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
文件中指定特定的域名。最好是最好是自主主機並使用單個主機名,可是同時生成一個請求映射,當腳本變化時,暴露給第四方調用和檢測。
再次檢查一遍 expires
,cache-control
,max-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!)。
在 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。
儘量經過 srcset
,sizes
和 <picture>
元素使用 響應式圖片。也能夠經過 <picture>
元素使用 WebP 格式的圖像(Chrom,Opera,Firefox soon支持),或者一個 JPEG 的回調(見 Andreas Bovens 的 code snippet)或者經過使用內容協商(使用 Accept
頭信息)。
Sketch 自己就支持 WebP 而且 WebP 圖像能夠經過使用 WebP 插件 從 PhotoShop 中導出。也有其餘選擇能夠使用,若是你使用 WordPress 或者 Joomla,也有能夠輕鬆支持 WebP 的擴展,例如 Optimus 和 Cache Enabler(經過 Cody Arsenault)
你能夠仍然使用 client hints,但仍須要得到一些瀏覽器支持。沒有足夠的資源支持響應式圖片?使用 斷點發生器 或者相似 Cloudinary 這樣的服務自動優化圖片。一樣,在許多狀況下,只使用 srcset
和 sizes
會有不錯的效果。
響應圖像斷點生成器自動生成圖像和標記生成。
如今有一個相當重要着陸頁,有一個特定的圖片的加載速度很是關鍵,確保 JPEGs 是漸進式的而且使用 Adept、 mozJPEG (經過操縱掃描級來改善開始渲染時間)或者 Guetzli 壓縮,谷歌新的開源編碼器重點是可以感官的性能,並借鑑 Zopfli 和 WebP。惟一的 不足 是:處理的時間慢(每百萬像素 CPU 一分鐘)。至於 png,咱們能夠使用 Pingo,和 svgo,對於 SVG 的處理,咱們使用 SVGO 或 SVGOMG
每個圖像優化的文章會說明,但始終會提醒要保持矢量資源乾淨和緊密。確保清理未使用的資源,刪除沒必要要的元數據,並減小圖稿中的路徑點數量(從而減小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 的 字體加載策略綜合指南 提供了十幾種更好的網頁字體發送選項
首先須要問一個問題,你是否能不使用 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 的字體樣式匹配器 用來解決因爲兩種字體之間的大小差別,最大限度地減小了佈局上的震動的問題。
當用戶請求頁面時,瀏覽器獲取 HTML 並構造 DOM,而後獲取 CSS 並構造 CSSOM,而後經過匹配 DOM 和 CSSOM 生成一個渲染樹。若是有任何的 JavaScript 須要解決,瀏覽器將不會開始渲染頁面,直到 JavaScript 解決完畢,這樣就會延遲渲染。 做爲開發人員,咱們必須明確告訴瀏覽器不要等待並當即開始渲染頁面。 爲腳本執行此操做的方法是使用 HTML 中的 defer
和 async
屬性。
事實證實,咱們 應該把 async
改成 defer
(由於 ie9 及如下不支持 async)。 另外,如上所述,限制第三方庫和腳本的影響,特別是使用社交共享按鈕和嵌入的 <iframe>
嵌入(如地圖)。 大小限制 有助於防止 JavaScript 庫過大:若是您不當心添加了大量依賴項,該工具將通知你並拋出錯誤。 你能夠使用 靜態社交分享按鈕(如經過 SSBG )和 靜態連接 來代替交互式地圖。
若是您須要延遲加載圖片、視頻、廣告腳本、A/B 測試腳本或任何其餘資源,則能夠使用 Intersection Observer API,它提供了一種方法異步觀察目標元素與 祖先元素或頂層文檔的視口。基本上,你須要建立一個新的 IntersectionObserver 對象,它接收一個回調函數和一組選項。 而後咱們添加一個目標來觀察。
當目標變得可見或不可見時執行回調函數,因此當它攔截視口時,能夠在元素變得可見以前開始採起一些行動。 事實上,咱們能夠精確地控制觀察者的回調什麼時候被調用,使用 rootMargin
和 threshold
(一個數字或者一個數字數組來表示目標可見度的百分比)。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 字體,以便鏈接速度太慢。
爲確保瀏覽器儘快開始渲染頁面,一般 會收集開始渲染頁面的第一個可見部分所需的全部 CSS(稱爲 「關鍵CSS」 或 「首屏 CSS」)並將其內聯添加到頁面的 <head>
中,從而減小往返。 因爲在慢啓動階段交換包的大小有限,因此關鍵 CSS 的預算大約是 14 KB。
若是超出這個範圍,瀏覽器將須要額外往返取得更多樣式。 CriticalCSS 和 Critical 能夠作到這一點。 你可能須要爲你使用的每一個模板執行此操做。 若是可能的話,考慮使用 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 解析器。
streams 常常被遺忘和忽略,它提供了異步讀取或寫入數據塊的接口,在任何給定的時間內,只有一部分數據可能在內存中可用。 基本上,只要第一個數據塊可用,它們就容許原始請求的頁面開始處理響應,並使用針對流進行優化的解析器逐步顯示內容。
咱們能夠從多個來源建立一個流。例如,您可讓服務器構建一個殼子來自於緩存,內容來自網絡的流,而不是提供一個空的 UI 外殼並讓它填充它。 正如 Jeff Posnick 指出的,若是您的 web 應用程序由 CMS 提供支持的,那麼服務器渲染 HTML 是經過將部分模板拼接在一塊兒來呈現的,該模型將直接轉換爲使用流式響應,而模板邏輯將從服務器複製而不是你的服務器。Jake Archibald 的 The Year of Web Streams 文章重點介紹瞭如何構建它。對於性能的提高是很是明顯的。
流式傳輸整個 HTML 響應的一個重要優勢是,在初始導航請求期間呈現的 HTML 能夠充分利用瀏覽器的流式 HTML 解析器。 在頁面加載以後插入到文檔中的 HTML 塊(與經過 JavaScript 填充的內容同樣常見)沒法利用此優化。
瀏覽器支持程度如何呢? 詳情請看這裏 Chrome 52+、Firefox 5七、Safari 和 Edge 支持此 API 而且服務器已經支持全部的 現代瀏覽器.
Save-Data
存儲數據嗎?特別是在新興市場工做時,你可能須要考慮優化用戶選擇節省數據的體驗。 Save-Data 客戶端提示請求頭 容許咱們爲成本和性能受限的用戶定製應用程序和有效載荷。 實際上,您能夠將 高 DPI 圖像的請求重寫爲低 DPI 圖像,刪除網頁字體和花哨的特效,關閉視頻自動播放,服務器推送,甚至更改提供標記的方式。
該頭部目前僅支持 Chromium,Android 版 Chrome 或 桌面設備上的 Data Saver 擴展。最後,你還能夠使用 service worker 和 Network Information API 來提供基於網絡類型的低/高分辨率的圖像。
使用 資源提示 來節約時間,如 dns-prefetch
(在後臺執行 DNS 查詢),preconnect
(告訴瀏覽器在後臺進行鏈接握手(DNS, TCP, TLS)),prefetch
(告訴瀏覽器請求一個資源) 和 preload
(預先獲取資源而不執行他們)。
最近,咱們至少會使用 preconnect
和 dns-prefetch
,咱們會當心使用 prefetch
和 preload
;前者只能在你很是肯定用戶後續須要什麼資源的狀況下使用(相似於採購渠道)。注意,prerender
已被棄用,再也不被支持。
請注意,即便使用 preconnect
和 dns-prefetch
,瀏覽器也會對它將並行查找或鏈接的主機數量進行限制,所以最好是將它們根據優先級進行排序(感謝 Philip!)。
事實上,使用資源提示多是最簡單的提升性能的方法,它確實頗有效。何時該使用呢?Addy Osmani 已經作了解釋,咱們應該預加載肯定將在當前頁面中使用的資源。預獲取可能用於將來頁面的資源,例如用戶還沒有訪問的頁面所需的 Webpack 包。
Addy 的關於 Chrome 中加載優先級的文章展現了 Chrome 是如何精確地解析資源提示的,所以一旦你決定哪些資源對頁面渲染比較重要,你就能夠給它們賦予比較高的優先級。你能夠在 Chrome DevTools 網絡請求表格(或者 Safari Technology Preview)中啓動「priority」列來查看你的請求的優先級。
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 響應標頭以前啓動預加載。
請注意:若是你正在使用 preload
,as
必須定義不然什麼都不會加載,還有,預加載字體時若是沒有 crossorigin
屬性將會獲取兩次
使用 CSS containment 隔離昂貴的組件 - 例如,限制瀏覽器樣式、用於非畫布導航的佈局和繪畫工做,第三方組件的範圍。確保在滾動頁面沒有延遲,或者當一個元素進行動畫時,持續地達到每秒 60 幀。若是這是不可能的,那麼至少要使每秒幀數持續保持在 60 到 15 的範圍。使用 CSS 的 will-change
通知瀏覽器哪一個元素的哪一個屬性將要發生變化。
此外,評估運行時渲染性能(例如,使用 DevTools)。能夠經過學習 Paul Lewis 免費的關於瀏覽器渲染優化的 Udacity 課程和 Emily Hayman 的文章優化網頁動畫和交互來入門。
一樣,咱們有 Sergey Chikuyonok 這篇文章關於如何正確使用 GPU 動畫。注意:對 GPU-composited 層的更改是代價最小的,若是你能經過「不透明」和「變形」來觸發合成,那麼你就是在正確的道路上。
組件以何種順序顯示在頁面上以及咱們如何給瀏覽器提供資源當然重要,可是咱們一樣也不能低估了感知性能的角色。這一律念涉及到等待的心理學,主要是讓用戶在其餘事情發生時保持忙碌。這就涉及到了感知管理,優先開始,提早完成和寬容管理。
這一切意味着什麼?在加載資源時,咱們能夠嘗試始終領先於客戶一步,因此將不少處理放置到後臺,響應會很迅速。讓客戶參與進來,咱們能夠用骨架屏幕(實例演示),而不是當沒有更多優化可作時、用加載指示,添加一些動畫/過渡欺騙用戶體驗。
在谷歌提出向更安全的網頁進軍以及Chrome 會把(全部使用 HTTP 的)網頁認定爲「不安全」的大環境下,遷移到HTTP/2是不可避免的。HTTP/2 從目前來看支持得很是好,而且,在某些場景下,使用 HTTP/2 會讓你大力出奇跡。一旦運行在 HTTPS 上,你至少可以在 service workers 和 server push 方面得到顯著的性能提高。
最終,谷歌計劃將全部 HTTP 頁面標記爲不安全的,並將有問題的 HTTPS 的 HTTP 安全指示器更改成紅色三角形。(圖片來源)
最耗時的任務將是遷移到 HTTPS,取決於你的 HTTP/1.1 用戶基礎有多大(即便用舊版操做系統或瀏覽器的用戶),你將不得不爲舊版的瀏覽器性能優化發送不一樣的構建版本,這須要你採用不一樣的構建流程。注意:開始遷移和新的構建過程可能會很棘手,並且耗費時間。接下來所講的內容,都是針對以前切過 HTTP/2 環境或者如今正準備切 HTTP/2 環境(的讀者)來展開的。
再次強調一下,須要對現階段正如何提供在你開始使用 HTTP/2 請求資源以前,須要搞清楚你之前是如何請求資源的。另外須要你在載入大模塊以及並行載入小模塊之間找到一個平衡點。 。最終,仍然是最好的請求就是沒有請求,然而咱們的目標是在快速傳輸資源和緩存之間找到一個好的平衡點。
一方面,你可能想要避免合併全部資源,而是把整個界面分解成許多小模塊,而後在構建過程當中壓縮這些小模塊,最後經過 「scount」 approach 的方法引用和並行加載這些小模塊。這樣的話,一個文件的更改就不須要從新下載整個樣式表或 JavaScript。這樣還能夠 最小化解析時間 ,並將單個頁面的負荷保持在較低的水平。
另外一方面,打包仍然很重要。首先,壓縮將獲益。在壓縮大文件的過程當中,藉助目錄重用的特色,達到優化性能的目的,而小的單獨的文件則不會。有標準的工做來解決這個問題,但如今還遠遠不夠。其次,瀏覽器還沒有爲這種工做流優化。例如,Chrome 將觸發進程間通訊(IPCs),與資源的數量成線性關係,所以頁面中若是包含數以百計的資源將會形成瀏覽器性能損失。
爲了得到使用 HTTP/2 最好的效果,能夠考慮使用漸進地加載 CSS,正如 Chrome 的 Jake Archibald 所推薦的。
你能夠嘗試漸進地加載 CSS。顯然,經過這樣作,這種作法不利於 HTTP/1.1 的用戶,所以在部署 HTTP/2 的過程當中,你可能須要針對不一樣的瀏覽器建立併發送該瀏覽器支持的 HTTP 協議的報頭,這還只是部署過程當中稍微複雜的地方。你能夠使用 HTTP/2 的 connection coalescing,可讓你在 HTTP/2 環境使用域名共享)來避開這些,但在實踐中實現這一目標是很困難的。
怎麼作呢?若是你運行在 HTTP/2 之上,發送 6-10 個包是個理想的折中(對舊版瀏覽器也不會太差)。對於你本身的網站,你能夠經過實驗和測量來找到最佳的折中。
不一樣的服務和 CDNs 可能對 HTTP/2 的支持狀況不同,可是能夠使用TLS 來查看你的可選服務,或者快速的查看你的服務的性能以及你想要其支持的特性。 TLS工具功能以下:
Is TLS Fast Yet? 容許你檢查你轉換到 HTTP /2 時你的服務器和 CDN 的選項。
經過在你的服務上啓動 OCSP stapling,你能夠加速 TLS 握手。在線證書狀態協議(OCSP)的提出是爲了替代證書註銷列表(CRL)協議。兩個協議都是用於檢查一個 SSL 證書是否已被撤回。可是,OCSP 協議不須要瀏覽器花時間下載而後在列表中搜索認證信息,所以減小了握手時間。
由於 IPv4 即將用完以及主要的移動網絡正在迅速採用 IPv6(美國已經達到50% 的 IPv6 使用閾值),將你的 DNS 更新到 IPv6 以應對將來是一個好的想法。只要確保在網絡上提供雙棧支持,就可讓 IPv6 和 IPv4 同時運行。畢竟,IPv6 不是向後兼容的。研究顯示,也是正由於 IPv6 自帶 NDP 以及路由優化,因此纔可以讓網站的載入速度提高10%到15%。
若是你使用 HTTP/2,請再次檢查,確保您的服務針對 HTTP 響應頭部實現 HPACK 壓縮以減小沒必要要的開銷。因爲 HTTP/2 服務相對較新,它們可能不徹底支持該規範,HPACK 就是一個例子。能夠使用 H2spec 這個偉大的(若是技術上很詳細)工具來檢查。HPACK做品。
H2spec (View large version) (Image source)
全部實現了 HTTP/2 的瀏覽器都在 TLS 上運行的大背景下,若是你遇到如下問題:
那麼,你須要
沒有什麼網絡性能優化能快過用戶機器上的本地緩存。若是你的網站運行在 HTTPS 上,那麼請參考使用 「Service Workers 的實用指南」。使用 service worker 中緩存靜態資源並存儲離線資源(甚至離線頁面)的目的,並且還會教你如何從用戶的設備裏面拿數據,也就是說,你如今是不須要經過網絡的方式去請求以前的數據。同時,參考 Jake 的 Offline Cookbook 和 Udacity 免費課程「離線 Web 應用程序」。瀏覽器支持?如上所述,它獲得了普遍支持 (Chrome、Firefox、Safari TP、Samsung Internet、Edge 17+),但無論怎麼說,它都是網絡。它有助於提升性能嗎?是的,Service Workers確實會提高性能。
在 Chrome 和 Firefox 中進行測試是不夠的。你應該去了解你的網站在代理瀏覽器和舊版瀏覽器中是如何工做的。例如,UC 瀏覽器和 Opera Mini,這些瀏覽器 在亞洲有大量的市場份額 (達到 35%)。在你感興趣的國家測量平均網絡速度從而避免在將來發現「大驚喜」。測試網絡節流,並仿真一個高 DPI 設備。BrowserStack 很不錯,但也要在實際設備上測試。
k6 可讓你像寫單元測試同樣編寫性能測試用例。
有一個WebPagetest私人的實例老是有利於快速和無限的測試。可是,一個帶有自動警報的連續監視工具將會給您提供更詳細的性能描述。設置您本身的用戶計時標記來度量和監視特定的業務度量。同時,考慮添加自動化性能迴歸警報來監控隨着時間而發生的變化。
使用 RUM 解決方案來監視性能隨時間的變化。對於自動化的類單元測試的負載測試工具,您能夠使用 k6 腳本 API。此外,能夠了解下 SpeedTracker、Lighthouse 和 Calibre。
這個列表很是全面,完成全部的優化可能須要很長時間。因此,若是你只有一個小時的時間來進行重大的改進,你會怎麼作?讓咱們把這一切歸結爲10個低掛的水果。顯然,在你開始以前和完成以後,測量結果,包括開始渲染時間以及在 3G 和電纜鏈接下的速度指數。
測量實際環境的體驗並設定適當的目標。一個好的目標是:第一次有意義的繪製 < 1 s,速度指數 < 1250,在慢速的 3G 網絡上的交互 < 5s,對於重複訪問,TTI < 2s。優化渲染開始時間和交互時間。
爲您的主模板準備關鍵的 CSS,並將其包含在頁面的 <head>
中。(你的預算是 14 KB)。對於 CSS/JS,文件大小不超過 170 KB gzipped(解壓後 0.8-1 MB)。
延遲加載儘量多的腳本,包括您本身的和第三方的腳本——特別是社交媒體按鈕、視頻播放器和耗時的 JavaScript 腳本。
添加資源提示,使用 dns-lookup
、preconnect
、prefetch
和 preload
加速傳輸。
分離 web 字體,並以異步方式加載它們(或切換到系統字體)。
優化圖像,並在重要頁面(例如登陸頁面)中考慮使用 WebP。
檢查 HTTP 緩存頭和安全頭是否設置正確。
在服務器上啓用 Brotli 或 Zopfli 壓縮。(若是作不到,不要忘記啓用 Gzip 壓縮。)
若是 HTTP/2 可用,啓用 HPACK 壓縮並開啓混合內容警告監控。若是您正在運行 LTS,也能夠啓用 OCSP stapling。
在 service worker 緩存中儘量多的緩存資產,如字體、樣式、JavaScript 和圖像。
記住了這個清單,您就已經爲任何類型的前端性能項目作好了準備。請隨意下載該清單的打印版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 對這篇文章的校對,一樣也感謝咱們出色的社區,分享了他們在性能優化工做中學習到的技術和經驗,供你們使用。大家真正的很是了不得!