- 原文地址:Front-End Performance Checklist 2018 - Part 2
- 原文做者:Vitaly Friedman
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:sakila1012
- 校對者:sunshine940326,xingqiwu55555
下面是前端性能問題的概述,你能夠參考以確保流暢的閱讀本文。javascript
依賴於你的組織優先性和戰略性,你可能想考慮使用谷歌的 AMP 和 Facebook 的 Instant Articles 或者蘋果的 Apple News。沒有它們,你能夠實現很好的性能,可是 AMP 確實提供了一個免費的內容分發網絡(CDN)的性能框架,而 Instant Articles 將提升你在 Facebook 上的知名度和表現。css
對於用戶而言,這些技術主要的優點是確保性能,可是有時他們寧願喜歡 AMP-/Apple News/Instant Pages 鏈路,也不肯是「常規」和潛在的臃腫頁面。對於之內容爲主的網站,主要處理不少第三方法內容,這些選擇極大地加速渲染的時間。html
對於網站的全部者而言優點是明顯的:在各個平臺規範的可發現性和增長搜索引擎的可見性。你也能夠經過把 AMP 做爲你的 PWA 數據源來構建漸進加強的 Web 體驗。缺點?顯然,在一個有圍牆的區域裏,開發者能夠創造並維持其內容的單獨版本,防止 Instant Articles 和 Apple News 沒有實際的URLs。(謝謝 Addy,Jeremy)前端
根據你擁有的動態數據量,你能夠將部份內容外包給靜態站點生成器,將其放在 CDN 中並從中提供一個靜態版本。所以能夠避免數據的請求。你甚至能夠選擇一個基於 CDN 的靜態主機平臺,將交互組件做爲加強來充實你的頁面 (jamstack)。java
注意,CDN 也能夠服務(卸載)動態內容。所以,限制你的 CDN 到靜態資源是沒必要要的。仔細檢查你的 CDN 是否進行壓縮和轉換(好比:圖像優化方面的格式,壓縮和調整邊緣的大小),智能 HTTP/2 交付,邊側包含,在 CDN 邊緣組裝頁面的靜態和動態部分(好比:離用戶最近的服務端),和其餘任務。node
知道你應該優先處理什麼是個好主意。管理你全部資產的清單(JavaScript,圖片,字體,第三方腳本和頁面中「昂貴的」模塊,好比:輪播圖,複雜的圖表和多媒體內容),並將它們劃分紅組。react
創建電子表格。針對傳統的瀏覽器,定義基本的_核心_體驗(好比:徹底可訪問的核心內容),針對多功能瀏覽器_提高_體驗(好比:豐富多彩的,完美的體驗)和其餘的(不是絕對須要並且能夠被延遲加載的資源,如 Web 字體、沒必要要的樣式、旋轉木馬腳本、視頻播放器、社交媒體按鈕、大型圖像。)。咱們在「Improving Smashing Magazine's Performance」發佈了一篇文章,上面詳細描述了該方法。android
雖然很老,但咱們仍然可使用 cutting-the-mustard 技術將核心經驗帶到傳統瀏覽器並加強對現代瀏覽器的體驗。嚴格要求加載的資源:優先加載核心傳統的,而後是提高的,最後是其餘的。該技術從瀏覽器版本中演變成了設備功能,這已經不是咱們如今能作的事了。webpack
例如:在發展中國家,廉價的安卓手機主要運行 Chrome,儘管他們的內存和 CPU 有限。這就是 PRPL 模式能夠做爲一個好的選擇。所以,使用設備內存客戶端提示頭,咱們將可以更可靠地針對低端設備。在寫做的過程當中,只有在 Blink 中才支持 header(Blink 支持客戶端提示)。由於設備存儲也有一個在 Chrome 中能夠調用的 JavaScript API,一種選擇是基於 API 的特性檢測,只在不支持的狀況下回退到 「符合標準」技術(謝謝,Yoav!)。ios
但咱們處理單頁面應用時,在你能夠渲染頁面時,你須要一些時間來初始化 app。尋找模塊和技術加快初始化渲染時間(例如:這裏是如何調試 React 性能,以及如何提升 Angular 性能),由於大多數性能問題來自於啓動應用程序的初始解析時間。
JavaScript 有成本,但不必定是文件大小會影響性能。解析和執行時間的不一樣很大程度依賴設備的硬件。在一個普通的手機上(Moto G4),僅解析 1MB (未壓縮的)的 JavaScript 大概須要 1.3-1.4 秒,會有 15 - 20% 的時間耗費在手機的解析上。在執行編譯過程當中,只是用在JavaScript準備平均須要 4 秒,在手機上繪排鬚要 11 秒。解釋:在低端移動設備上,解析和執行時間能夠輕鬆提升 2 至 5 倍。
Ember 最近推出了一個實驗,一種使用二進制模板巧妙的避免解析開銷的方式。這些模板不須要解析。(感謝,Leonardo!)
這就是檢查每一個 JavaScript 依賴性的關鍵,工具像 webpack-bundle-analyzer,Source Map Explorer 和 Bundle 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.
使用預編譯器來減輕從客戶端到服務端的渲染的開銷,所以快速輸出有用的結果。最後,考慮使用 Optimize.js 更快的加載,用快速地調用的函數(儘管,它可能不須要)。
Tree-shaking 是一種經過只加載生產中確實被使用的代碼和在 Webpack 中清除無用部分,來整理你構建過程的方法。使用 Webpack 3 和 Rollup,咱們還能夠提高做用域容許工具檢測 import
連接以及能夠轉換成一個內聯函數,不影響代碼。有了 Webpack 4,你如今可使用 JSON Tree Shaking。UnCSS 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 的從[快速默認:現代加載的最佳實踐](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 的負載。
研究 JavaScript 引擎在用戶基礎中占主導地位,而後探索優化它們的方法。例如,當優化的 V8 引擎是用在 Blink 瀏覽器,Node.js 運行和電子,對每一個腳本充分利用腳本流。一旦下載開始,它容許 async
或 defer scripts
在一個單獨的後臺線程進行解析,所以在某些狀況下,提升頁面加載時間達 10%。實際上,在 <head>
中使用 <腳本延遲>
,以至於瀏覽器更早地能夠發現資源,而後在後臺線程中解析它。
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 和交叉觀察;這將使廣告嵌入 iframe 的同時仍然調度事件或須要從 DOM 獲取信息(例如廣告知名度)。注意新的策略如特徵策略),資源的大小限制,CPU 和帶寬優先級限制損害的網絡功能和會減慢瀏覽器的腳本,例如:同步腳本,同步 XHR 請求,document.write 和超時的實現。
爲了壓測第三方,在 DevTools 上自底向上概要地檢查頁面的性能,測試若是一個請求被阻塞了會發生什麼或者對於後面的請求有超時限制,你可使用 WebPageTest's Blackhole 服務器 72.66.115.13
,同時能夠在你的 hosts
文件中指定特定的域名。最好是自我主機和使用一個單一的主機名,可是同時生成一個請求映射,當腳本變化時,暴露給第四方調用和檢測。
圖片信用:Harry Roberts
再次檢查一遍 expires
,cache-control
,max-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!)。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。