[譯] 2018 前端效能優化清單 - 第 2 部分

sakila發表於2018-01-23

2018 前端效能優化清單 - 第 2 部分

下面是前端效能問題的概述,你可以參考以確保流暢的閱讀本文。


  1. 你會在你的專案中使用 AMP 和 Instant Articles 麼?

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

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

對於網站的所有者而言優勢是明顯的:在各個平臺規範的可發現性和增加搜尋引擎的可見性。你也可以通過把 AMP 作為你的 PWA 資料來源來構建漸進增強的 Web 體驗。缺點?顯然,在一個有圍牆的區域裡,開發者可以創造並維持其內容的單獨版本,防止 Instant Articles 和 Apple News 沒有實際的URLs。(謝謝 Addy,Jeremy

  1. 明智地選擇你的 CDN

根據你擁有的動態資料量,你可以將部分內容外包給靜態站點生成器,將其放在 CDN 中並從中提供一個靜態版本。因此可以避免資料的請求。你甚至可以選擇一個基於 CDN 的靜態主機平臺,將互動元件作為增強來充實你的頁面 (jamstack)。

注意,CDN 也可以服務(解除安裝)動態內容。因此,限制你的 CDN 到靜態資源是不必要的。仔細檢查你的 CDN 是否進行壓縮和轉換(比如:影象優化方面的格式,壓縮和調整邊緣的大小),智慧 HTTP/2 交付,邊側包含,在 CDN 邊緣組裝頁面的靜態和動態部分(比如:離使用者最近的服務端),和其他任務。

構建優化

  1. 分清輕重緩急

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

建立電子表格。針對傳統的瀏覽器,定義基本的_核心_體驗(比如:完全可訪問的核心內容),針對多功能瀏覽器_提升_體驗(比如:豐富多彩的,完美的體驗)和其他的(不是絕對需要而且可以被延遲載入的資源,如 Web 字型、不必要的樣式、旋轉木馬指令碼、視訊播放器、社交媒體按鈕、大型影象。)。我們在“Improving Smashing Magazine's Performance”釋出了一篇文章,上面詳細描述了該方法。

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

雖然很老,但我們仍然可以使用 cutting-the-mustard 技術將核心經驗帶到傳統瀏覽器並增強對現代瀏覽器的體驗。嚴格要求載入的資源:優先載入核心傳統的,然後是提升的,最後是其他的。該技術從瀏覽器版本中演變成了裝置功能,這已經不是我們現在能做的事了。

例如:在發展中國家,廉價的安卓手機主要執行 Chrome,儘管他們的記憶體和 CPU 有限。這就是 PRPL 模式可以作為一個好的選擇。因此,使用裝置記憶體客戶端提示頭,我們將能夠更可靠地針對低端裝置。在寫作的過程中,只有在 Blink 中才支援 header(Blink 支援客戶端提示)。因為裝置儲存也有一個在 Chrome 中可以呼叫的 JavaScript API,一種選擇是基於 API 的特性檢測,只在不支援的情況下回退到 “符合標準”技術(謝謝Yoav!)。

  1. 解析 JavaScript 的代價很大,應保持其較小

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

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

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

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

<figure class="video-container"><iframe src="https://player.vimeo.com/video/249525818" width="640" height="384" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
複製程式碼

Webpack Bundle Analyzer visualizes JavaScript dependencies.

  1. 你使用預編譯器麼?

使用預編譯器減輕從客戶端服務端的渲染的開銷,因此快速輸出有用的結果。最後,考慮使用 Optimize.js 更快的載入,用快速地呼叫的函式(儘管,它可能不需要)。

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

Tree-shaking 是一種通過只載入生產中確實被使用的程式碼和在 Webpack 中清除無用部分,來整理你構建過程的方法。使用 Webpack 3 和 Rollup,我們還可以提升作用域允許工具檢測 import 連結以及可以轉換成一個行內函數,不影響程式碼。有了 Webpack 4,你現在可以使用 JSON Tree ShakingUnCSS or Helium 可以幫助你去刪除未使用 CSS 樣式。

而且,你想考慮學習如何編寫有效的 CSS 選擇器以及如何避免臃腫和開銷浪費的樣式。感覺好像超越了這個?你也可以使用 Webpack 縮短類名和在編譯時使用作用域孤立來動態地重新命名 CSS 類名

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

在哪裡定義分離點?通過追蹤使用哪些 CSS/JavaScript 塊和哪些沒有使用。Umar Hansa 解釋了你如何可以使用 Devtools 程式碼覆蓋率來實現。

如果你沒有使用 Webpack,值得注意的是相比於 Browserify 輸出結果 Rollup 展現的更加優秀。當使用 Rollup 時,我們會想要檢視 Rollupify,它可以轉化 ECMAScript 2015 modules 為一個大的 CommonJS module ——因為取決於打包工具和模組載入系統的選擇,小的模組會有令人驚訝的高效能開銷

Addy Osmani 的'預設快速:現代負載最佳實踐'

Addy Osmani 的從[快速預設:現代載入的最佳實踐](https://speakerdeck.com/addyosmani/fast-by-default-modern-loading-best-practices)。幻燈片76。

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

對於 loadsh,使用 babel-plugin-lodash將會載入你僅僅在原始碼中使用的。這樣將會很大程度減輕 JavaScript 的負載。

  1. 利用目標 JavaScript 引擎的優化。

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

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 和交叉觀察;這將使廣告嵌入 iframe 的同時仍然排程事件或需要從 DOM 獲取資訊(例如廣告知名度)。注意新的策略如特徵策略),資源的大小限制,CPU 和頻寬優先順序限制損害的網路功能和會減慢瀏覽器的指令碼,例如:同步指令碼,同步 XHR 請求,document.write 和超時的實現。

為了壓測第三方,在 DevTools 上自底向上概要地檢查頁面的效能,測試如果一個請求被阻塞了會發生什麼或者對於後面的請求有超時限制,你可以使用 WebPageTest's 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,該頭部針對被標記指紋的靜態資源設計,避免資源被重新驗證(截至 2017年12月,在 FireFox,Edge 和 Safari 中支援;只有 FireFox 在 HTTPS 中支援)。你也可以使用 Heroku 的 HTTP 快取頭部,Jake Archibald 的 "Caching Best Practices" ,以及 Ilya Grigorik 的 HTTP caching primer 作為指導。而且,注意不同的頭部,尤其是在關係到 CDN 時,並且注意關鍵頭部有助於避免在新請求稍有差異時進行額外的驗證,但從以前請求標準,並不是必要的(感謝Guy!)。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章