- 原文地址:Front-End Performance Checklist 2019 — 4
- 原文做者:Vitaly Friedman
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:Ivocin
- 校對者:ziyin feng,weibinzhu
讓 2019 來得更迅速吧!你如今閱讀的是 2019 年前端性能優化年度總結,始於 2016。javascript
要了解你首先要處理什麼。列出你所有的靜態資源清單(JavaScript、圖片、字體、第三方腳本以及頁面上的大模塊:如輪播圖、複雜的信息圖表和多媒體內容),並將它們分組。css
新建一個電子表格。定義舊版瀏覽器的基本核心體驗(即徹底可訪問的核心內容)、現代瀏覽器的加強體驗(即更加豐富的完總體驗)以及額外功能(能夠延遲加載的非必需的資源:例如網頁字體、沒必要要的樣式、輪播腳本、視頻播放器、社交媒體按鈕和大圖片)。不久前,咱們發表了一篇關於「提高 Smashing 雜誌網站性能」的文章,文中詳細描述了這種方法。html
在優化性能時,咱們須要肯定咱們的優先事項。當即加載核心體驗,而後加載加強體驗,最後加載額外功能。前端
現在,咱們仍然可使用符合最低要求(cutting-the-mustard)技術 將核心體驗發送到舊版瀏覽器,併爲現代瀏覽器提供加強體驗。(譯者注:關於 cutting-the-mustard 出處能夠參考這篇文章。)該技術的一個更新版本將使用 ES2015 + 語法 <script type="module">
。現代瀏覽器會將腳本解釋爲 JavaScript 模塊並按預期運行它,而舊版瀏覽器沒法識別該屬性並忽略它,由於它是未知的 HTML 語法。java
如今咱們須要謹記的是,單獨的功能檢測不足以作出該發送哪些資源到該瀏覽器的明智決定。就其自己而言,符合最低要求 從瀏覽器版本中推斷出設備的能力,今天已經再也不有效了。node
例如,發展中國家的廉價 Android 手機主要使用 Chrome 瀏覽器,儘管設備的內存和 CPU 功能有限,但其仍然達到了使用符合最低要求技術的標準。最終,使用設備內存客戶端提示報頭,咱們將可以更可靠地定位低端設備。在本文寫做時,僅在 Blink 中支持該報頭(一般用於客戶端提示)。因爲設備內存還有一個已在 Chrome 中提供的 JavaScript API,所以基於該 API 進行功能檢測是一個選擇,而且只有在不支持時纔會再來使用符合最低要求技術(感謝 Yoav!)。react
在處理單頁面應用程序時,咱們須要一些時間來初始化應用程序,而後才能渲染頁面。你的設置須要你的自定義解決方案,但能夠留意可以加快首次渲染的模塊和技術。例如,如何調試 React 性能、消除常見的 React 性能問題,以及如何提升 Angular 的性能。一般,大多數性能問題都來自啓動應用程序的初始解析時間。jquery
JavaScript 有一個解析的成本,但不多僅是因爲文件大小一個因素影響性能。解析和執行時間根據設備的硬件的不一樣有很大差別。在普通電話(Moto G4)上,1MB(未壓縮)JavaScript 的解析時間約爲 1.3-1.4s,移動設備上有 15-20% 的時間用於解析。在遊戲中編譯,僅僅在準備 JavaScript 就平均耗時 4 秒,在移動設備上首次有效繪製(First Meaningful Paint )以前大約須要 11 秒。緣由:在低端移動設備上,解析和執行時間很容易高出 2-5 倍。android
爲了保證高性能,做爲開發人員,咱們須要找到編寫和部署更少許 JavaScript 的方法。這就是爲何要詳細檢查每個 JavaScript 依賴關係的緣由。webpack
有許多工具能夠幫助你作出有關依賴關係和可行替代方案影響的明智決策:
有一種有趣方法能夠用來避免解析成本,它使用了 Ember 在 2017 年推出的二進制模板。使用該模板,Ember 用 JSON 解析代替 JavaScript 解析,這可能更快。(感謝 Leonardo,Yoav!)
衡量 JavaScript 解析和編譯時間。咱們可使用綜合測試工具和瀏覽器跟蹤來跟蹤解析時間,瀏覽器實現者正在談論未來把基於 RUM 的處理時間暴露出來。也能夠考慮使用 Etsy 的 DeviceTiming,這是一個小工具,它容許你使用 JavaScript 在任何設備或瀏覽器上測量解析和執行時間。
底線:雖然腳本的大小很重要,但它並非一切。隨着腳本大小的增加,解析和編譯時間不必定會線性增長。
搖樹(tree-shaking)是一種在 webpack 中清理構建過程的方法,它僅將實際生產環境使用的代碼打包,並排除沒有使用的導入模塊。使用 webpack 和 rollup,還可使用做用域提高(scope hoisting),做用域提高使得 webpack 和 rollup 能夠檢測 import
鏈能夠展開的位置,並將其轉換爲一個內聯函數,而且不會影響代碼。使用 webpack,咱們也可使用 JSON Tree Shaking。
此外,你可能須要考慮學習如何編寫高效的 CSS 選擇器,以及如何避免臃腫且耗時的樣式。若是你但願更進一步,你還可使用 webpack 來縮短 class 名,並使用做用域隔離在編譯時動態重命名 CSS class 名。
代碼拆分(code-splitting)是另外一個 webpack 功能,它將你的代碼庫拆分爲按需加載的「塊」。並不是全部的 JavaScript 都必須當即下載、解析和編譯。在代碼中定義分割點後,webpack 能夠處理依賴項和輸出文件。它可以保持較小體積的初始下載,並在應用程序請求時按需請求代碼。Alexander Kondrov 有一個使用 webpack 和 React 應用代碼分割的精彩介紹。
考慮使用 preload-webpack-plugin,它接受代碼拆分的路由,而後提示瀏覽器使用 <link rel="preload">
或 <link rel="prefetch">
預加載它們。Webpack 內聯指令還能夠控制 preload
/prefetch
。
在哪裏定義分割點呢?經過跟蹤代碼查看使用了哪些 CSS/JavaScript 包,沒有使用哪些包。Umar Hansa 解釋了如何使用 Devtools 的代碼覆蓋率工具來實現它。
若是你沒有使用 webpack,請注意 rollup 顯示的結果明顯優於 Browserify 導出。雖然咱們參與其中,但你可能須要查看 rollup-plugin-closure-compiler 和 rollupify,它將 ECMAScript 2015 模塊轉換爲一個大型 CommonJS 模塊 —— 由於根據你的包和模塊系統的選擇,小模塊可能會有驚人高的成本。
爲了減小對首次可交互時間(Time-to-Interactive)的負面影響,考慮將高耗時的 JavaScript 放到 Web Worker 或經過 Service Worker 來緩存。
隨着代碼庫的不斷增加,UI 性能瓶頸將會出現,進而會下降用戶的體驗。主要緣由是 DOM 操做與主線程上的 JavaScript 一塊兒運行。經過 web worker,咱們能夠將這些高耗時的操做移動到後臺進程的另外一線程上。Web worker 的典型用例是預獲取數據和漸進式 Web 應用程序,提早加載和存儲一些數據,以便你在以後須要時使用它。並且你可使用 Comlink 簡化主頁面和 worker 之間的通訊。仍然還有一些工做要作,但咱們已經作了不少了。
Workerize 讓你可以將模塊移動到 Web Worker 中,自動將導出的函數映射爲異步代理。若是你正在使用 webpack,你可使用 workerize-loader。或者,也能夠試試 worker-plugin。
請注意,Web Worker 無權訪問 DOM,由於 DOM 不是「線程安全的」,並且它們執行的代碼須要包含在單獨的文件中。
咱們還能夠將 JavaScript 轉換爲 WebAssembly,這是一種二進制指令格式,可使用 C/C++/Rust 等高級語言進行編譯。它的瀏覽器支持很是出色,最近它變得可行了,由於 JavaSript 和 WASM 之間的函數調用速度變得愈來愈快,至少在 Firefox 中是這樣。
在實際場景中,JavaScript 彷佛在較小的數組大小上比 WebAssembly 表現更好,而 WebAssembly 在更大的數組大小上比 JavaScript 表現更好。對於大多數 Web 應用程序,JavaScript 更適合,而 WebAssembly 最適合用於計算密集型 Web 應用程序,例如 Web 遊戲。可是,若是切換到 WebAssembly 可否得到顯着的性能改進,則可能值得研究。
若是你想了解有關 WebAssembly 的更多信息:
Lin Clark 爲 WebAssembly 撰寫了一個全面的系列文章,Milica Mihajlija 概述了如何在瀏覽器中運行原生代碼、爲何要這樣作、以及它對 JavaScript 和 Web 開發的將來意味着什麼。
Google Codelabs 提供了一份 WebAssembly 簡介,這是一個 60 分鐘的課程,你將學習如何使用原生代碼 —— 使用 C 並將其編譯爲 WebAssembly,而後直接在 JavaScript 調用它。
Alex Danilo 在他的 Google I/O 2017 演講中解釋了 WebAssembly 及其工做原理。此外,Benedek Gagyi 分享了一個關於 WebAssembly 的實際案例研究,特別是團隊如何將其用做 iOS、Android 和網站的 C++ 代碼庫的輸出格式。
Milica Mihajlija 提供了 WebAssembly 的工做原理及其有用的緣由的概述。 (預覽大圖)
使用 AOT(ahead-of-time)編譯器將一些客戶端渲染放到服務器,從而快速輸出可用結果。最後,考慮使用 Optimize.js 來加速初始化加載時間,它包裝了須要當即調用的函數(儘管如今這可能不是必需的了)。
來自默認快速:現代加載最佳實踐,做者是獨一無二的 Addy Osmani。幻燈片第 76 頁。
因爲 ES2015 在現代瀏覽器中獲得了很是好的支持,咱們能夠使用 babel-preset-env
,僅轉義還沒有被咱們的目標瀏覽器支持的那些 ES2015 + 特性。而後設置兩個構建,一個在 ES6 中,一個在 ES5 中。如上所述,如今全部主流瀏覽器都支持 JavaScript 模塊,所以使用 script type =「module」
讓支持 ES 模塊的瀏覽器加載支持 ES6 的文件,而舊瀏覽器可使用 script nomodule
加載支持 ES5 的文件。咱們可使用 Webpack ESNext Boilerplate 自動完成整個過程。
請注意,如今咱們能夠編寫基於模塊的 JavaScript,它能夠原生地在瀏覽器裏運行,無需編譯器或打包工具。<link rel="modulepreload">
header 提供了一種提早(和高優先級)加載模塊腳本的方法。基本上,它可以很好地最大化使用帶寬,經過告訴瀏覽器它須要獲取什麼,以便在這些長的往返期間不會卡頓。此外,Jake Archibald 發佈了一篇詳細的文章,其中包含了須要牢記的 ES 模塊相關內容,值得一讀。
對於 lodash,使用 babel-plugin-lodash
,經過它能夠只加載你在源代碼中使用的模塊。你的其餘依賴也可能依賴於其餘版本的 lodash,所以將通用 lodash requires
轉換爲特定須要的功能,以免代碼重複。這可能會爲你節省至關多的 JavaScript 負載。
Shubham Kanodia 撰寫了一份詳細的關於智能打包的低維護指南:如何在生產環境中實現僅僅將遺留代碼推送到老版本瀏覽器上,裏面還有一些你能夠直接拿來用的代碼片斷。
Jake Archibald 發佈了一篇詳細的文章,其中包含了 須要牢記的 ES 模塊相關內容,例如:內聯腳本會被推遲,直到正在阻塞的外部腳本和內聯腳本獲得執行。(預覽大圖)
咱們但願經過網絡發送必要的 JavaScript,但這意味着須要更加集中精力而且細粒度地關注這些靜態資源的傳送。前一陣子 Philip Walton 介紹了差別化服務的想法。該想法是編譯和提供兩個獨立的 JavaScript 包:「常規」構建,帶有 Babel-transforms 和 polyfill 的構建,只提供給實際須要它們的舊瀏覽器,以及另外一個沒有轉換和 polyfill 的包(具備相同功能)。
結果,經過減小瀏覽器須要處理的腳本數量來幫助減小主線程的阻塞。Jeremy Wagner 在 2019 年發佈了一篇關於差別服務以及如何在你的構建管道中進行設置的綜合文章,從設置 babel 到你須要在 webpack 中進行哪些調整,以及完成全部這些工做的好處。
老項目充斥着陳舊和過期的代碼。從新查看你的依賴項,評估重構或重寫最近致使問題的遺留代碼所需的時間。固然,它始終是一項重大任務,可是一旦你瞭解了遺留代碼的影響,就能夠從增量解耦開始。
首先,設置指標,跟蹤遺留代碼調用的比率是保持不變或是降低,而不是上升。公開阻止團隊使用該庫,並確保你的 CI 可以警告開發人員,若是它在拉取請求(pull request)中使用。Polyfill 能夠幫助將遺留代碼轉換爲使用標準瀏覽器功能的重寫代碼庫。
Chrome 中的 CSS 和 JavaScript 代碼覆蓋率可讓你瞭解哪些代碼已執行/已應用,哪些代碼還沒有執行。你能夠開始記錄覆蓋範圍,在頁面上執行操做,而後瀏覽代碼覆蓋率結果。一旦你檢測到未使用的代碼,找到那些模塊並使用 import()
延遲加載(參見整個線程)。而後重複覆蓋配置文件並驗證它如今在初始加載時發送的代碼是否變少了。
你可使用 Puppeteer 以編程方式收集代碼覆蓋率,Canary 也可以讓你導出代碼覆蓋率結果。正如 Andy Davies 提到的那樣,你可能但願同時收集現代和舊版瀏覽器的代碼覆蓋率。Puppeteer 還有許多其餘用例,例如,自動視差或監視每一個構建的未使用的 CSS。
此外,purgecss、UnCSS 和 Helium 能夠幫助你從 CSS 中刪除未使用的樣式。若是你不肯定是否在某處使用了可疑的代碼,能夠遵循 Harry Roberts 的建議:爲該 class 建立 1×1px 透明 GIF 並將其放入 dead/
目錄,例如:/assets/img/dead/comments.gif
。而後,將該特定圖像設置爲 CSS 中相應選擇器的背景,而後靜候幾個月,查看該文件可否出如今你的日誌中。若是日誌裏沒出現該條目,則沒有人使用該遺留組件:你能夠繼續將其所有刪除。
對於愛冒險的人,你甚至能夠經過使用 DevTools 監控 DevTools,經過一組頁面自動收集未使用的 CSS。
正如 Addy Osmani 指出的那樣,當你只須要一小部分時,你極可能會發送完整的 JavaScript 庫,以及提供給不須要它們的瀏覽器的過期 polyfill,或者只是重複代碼。爲避免額外開銷,請考慮使用 webpack-libs-optimization,在構建過程當中刪除未使用的方法和 polyfill。
將打包審計添加到常規工做流程中。有一些你在幾年前添加的重型庫的輕量級替代品,例如:Moment.js 能夠用 date-fns 或 Luxon 代替。Benedikt Rötsch 的研究代表,從 Moment.js 到 date-fns 的轉換可能會使 3G 和低端手機上的首次繪製時間減小大約 300ms。
這就是 Bundlephobia 這樣的工具能夠幫助你找到在程序包中添加 npm 包的成本。你甚至能夠將這些成本與 Lighthouse Custom Audit 相結合。這也適用於框架。經過刪除或減少 Vue MDC 適配器(Vue 的 Material 組件),樣式能夠從 194KB 降至 10KB。
喜歡冒險嗎?你能夠看看Prepack。它將 JavaScript 編譯爲等效的 JavaScript 代碼,但與 Babel 或 Uglify 不一樣,它容許你編寫正常的 JavaScript 代碼,並輸出運行速度更快的等效 JavaScript 代碼。
除了傳送整個框架包以外,你甚至能夠修剪框架並將其編譯爲不須要額外代碼的原始 JavaScript 包。Svelte 作到了,Rawact Babel 插件也是如此,它在構建時將 React.js 組件轉換爲原生 DOM 操做。 爲何?好吧,正如維護者解釋的那樣:「React-dom 包含能夠渲染的每一個可能組件/ HTMLElement 的代碼,包括用於增量渲染、調度、事件處理等的代碼。可是有些應用程序不須要全部這些功能(在初始頁面加載時)。對於此類應用程序,使用原生 DOM 操做構建交互式用戶界面多是有意義的。」
在 Benedikt Rötsch 的文章中,他表示,從 Moment.js 到 date-fns 的轉換會使 3G 和低端手機上的首次繪製時間減小大約 300ms。(預覽大圖)
咱們可使用啓發式方法來決定什麼時候預加載 JavaScript 代碼塊。Guess.js 是一組工具和庫,它使用 Google Analytics 的數據來肯定用戶最有可能從給定頁面訪問哪一個頁面。根據從 Google Analytics 或其餘來源收集的用戶導航模式,Guess.js 構建了一個機器學習模型,用於預測和預獲取每一個後續頁面中所需的 JavaScript。
所以,每一個交互元素都接收參與的機率評分,而且基於該評分,客戶端腳本決定提早預獲取資源。你能夠將該技術集成到 Next.js 應用程序、Angular 和 React 中,還有一個 webpack 插件可以自動完成設置過程。
顯然,你可能會讓瀏覽器預測到使用不須要的數據從而預獲取到不須要的頁面,所以最好在預獲取請求的數量上保持絕對保守。一個好的用例是預獲取結帳中所需的驗證腳本,或者當一個關鍵的 CTA(call-to-action)進入視口時的推測性預獲取。
須要不太複雜的東西?Quicklink 是一個小型庫,可在空閒時自動預獲取視口中的連接,以便加快下一頁導航的加載速度。可是,它也考慮了數據流量,所以它不會在 2G 網絡或者 Data-Saver
打開時預獲取數據。
研究哪些 JavaScript 引擎在你的用戶羣中占主導地位,而後探索針對這些引擎的優化方法。例如,在爲 Blink 內核瀏覽器、Node.js 運行時和 Electron 中使用的 V8 進行優化時,使用腳本流來處理龐大的腳本。它容許在下載開始時在單獨的後臺線程上解析 async
或 defer scripts
,所以在某些狀況下能夠將頁面加載時間減小多達 10%。實際上,在 <head>
裏使用 <script defer>
,以便瀏覽器能夠提早發現資源,而後在後臺線程上解析它。
警告:Opera Mini 不支持腳本延遲,因此若是你正在爲印度或非洲開發,defer
將被忽略,這會致使阻止渲染,直到腳本執行完爲止(感謝 Jeremy!)。
漸進式啓動意味着使用服務器端渲染來得到快速的首次有效繪製,但也包括一些最小的 JavaScript,以保持首次交互時間接近首次有效繪製時間。
在這兩種狀況下,咱們的目標應該是設置漸進式啓動:使用服務器端渲染來得到快速的首次有效繪製,但也包括一些最小的必要 JavaScript,以保持首次交互時間接近首次有效繪製時間。若是 JavaScript 在首次有效繪製以後來得太晚,瀏覽器可能會在解析、編譯和執行後期發現的 JavaScript 時鎖定主線程,從而給站點或應用程序的交互帶來枷鎖。
爲避免這種狀況,請始終將函數執行分解爲獨立的異步任務,並儘量使用 requestIdleCallback
。考慮使用 webpack 的動態 import()
支持,延遲加載 UI 的部分,下降加載、解析和編譯成本,直到用戶真正須要它們(感謝 Addy!)。
從本質上講,首次可交互時間(TTI)告訴咱們導航和交互之間的時間。經過查看初始內容渲染後的前五秒窗口來定義度量標準,其中任何 JavaScript 任務都不會超過 50 毫秒。若是發生超過 50 毫秒的任務,則從新開始搜索五秒鐘窗口。所以,瀏覽器將首先假設它已到達交互狀態,而後切換到凍結狀態,最終切換回交互狀態。
一旦咱們到達交互狀態,在按需或在時間容許的狀況下,就能夠啓動應用程序的非必要部分。不幸的是,正如 Paul Lewis 所注意到的那樣,框架一般沒有提供給開發者優先級的概念,所以大多數庫和框架都難以實現漸進式啓動。若是你有時間和資源,請使用此策略最終提高性能。
那麼,客戶端仍是服務器端?若是用戶沒有明顯的好處,客戶端渲染可能不是真正必要的 —— 實際上,服務器端渲染的 HTML 可能更快。也許你甚至能夠使用靜態站點生成器預渲染一些內容,並將它們直接推送到 CDN,並在頂部添加一些 JavaScript。
將客戶端框架的使用限制爲絕對須要它們的頁面。若是作得很差,服務器渲染和客戶端渲染是一場災難。考慮在構建時預渲染和動態 CSS 內聯,以生成生產就緒的靜態文件。Addy Osmani 就可能值得關注的 JavaScript 成本發表了精彩的演講。
經過全部性能優化,咱們一般沒法控制來自業務需求的第三方腳本。第三方腳本指標不受最終用戶體驗的影響,所以一般一個腳本最終會調用使人討厭的冗長的第三方腳本,從而破壞了專門的性能工做。爲了控制和減輕這些腳本帶來的性能損失,僅僅異步加載它們(多是經過延遲)並經過資源提示(如 dns-prefetch
或 preconnect
)加速它們是不夠的。
正如 Yoav Weiss 在他關於第三方腳本的必讀觀點中所解釋的那樣,在許多狀況下,這些腳本會下載動態的資源。資源在頁面加載之間發生變化,所以咱們沒有必要知道從哪些主機下載資源以及這些資源是什麼。
你有哪些選擇方案?考慮使用 service worker,經過超時競爭資源下載,若是資源在特定超時內沒有響應,則返回空響應以告知瀏覽器繼續解析頁面。你還能夠記錄或阻止未成功或不符合特定條件的第三方請求。若是能夠,請從你本身的服務器而不是從供應商的服務器加載第三方腳本。
Casper.com 發佈了一個詳細的案例研究,說明他們如何經過自託管的 Optimizely 網站響應時間減小了 1.7 秒。這多是值得的。(圖片來源)(預覽大圖)
另外一種選擇是創建內容安全策略(CSP)以限制第三方腳本的影響,例如:不容許下載音頻或視頻。最好的選擇是經過 <iframe>
嵌入腳本,以便腳本在 iframe 的上下文中運行,所以第三方腳本沒法訪問頁面的 DOM,也沒法在你的域上運行任意代碼。使用 sandbox
屬性能夠進一步約束 iframe,那樣你就能夠禁用一切 iframe 可能執行的任何功能,例如:防止腳本運行、阻止警報、表單提交、插件、訪問頂部導航等。
好比,可能必須使用 <iframe sandbox="allow-scripts">
來運行腳本。每一個限制均可以經過 sandbox
屬性上的各類 allow
值來解除(幾乎全部的瀏覽器都受支持),所以將它們限制在應該容許的最低限度。
考慮使用 Intersection Observer;這將使廣告仍然在 iframe 中,可是能夠調度事件或從 DOM 獲取所需信息(例如,廣告可見性)。能夠關注一些新的策略,例如功能策略,資源大小限制和 CPU/帶寬優先級,以限制可能會下降瀏覽器速度的有害 Web 功能和腳本,例如:同步腳本、同步 XHR 請求、document.write
和過期的實現。
要對第三方進行壓力測試,請檢查 DevTools 中性能配置文件頁面中的自下而上的摘要,測試若是請求被阻止或超時的狀況會發生什麼 —— 對於後者,你可使用 WebPageTest 的 Blackhole 服務器 blackhole.webpagetest.org
,它能夠將特定域指向你的 hosts
文件。最好是自託管並使用單一主機名,但也能夠生成一個請求映射,該映射公開第四方調用並檢測腳本什麼時候更改。你可使用 Harry Roberts 的方法審覈第三方,並生成相似這樣的電子表格。Harry 還在他關於第三方性能和審計的討論中解釋了審計工做流程。
圖片來源: Harry Roberts
仔細檢查是否已正確設置 expires
、max-age
、cache-control
和其餘 HTTP 緩存頭。一般,資源不管在短期內(若是它們可能會更改)仍是無限期(若是它們是靜態的)狀況下都是可緩存的 —— 你只需在須要時在 URL 中更改它們的版本。禁用 Last-Modified
標頭,由於任何帶有它的靜態資源都將致使帶有 If-Modified-Since
標頭的條件請求,即便資源位於緩存中也是如此。Etag
也是如此。
使用使用專爲指紋靜態資源設計的 Cache-control:immutable
,以免從新驗證(截至 2018 年 12 月,Firefox、Edge 和 Safari 都已經支持該功能; Firefox 僅支持 https://
事務)。事實上,「在 HTTP 存檔中的全部頁面中,2% 的請求和 30% 的網站彷佛包含至少 1 個不可變響應。此外,大多數使用它的網站都設置了具備較長新鮮生命週期的靜態資源。」
還記得 stale-while-revalidate 嗎?你可能知道,咱們使用 Cache-Control
響應頭指定緩存時間,例如:Cache-Control: max-age=604800
。通過 604800 秒後,緩存將從新獲取所請求的內容,從而致使頁面加載速度變慢。經過使用 stale-while-revalidate
能夠避免這種速度變慢的問題。它本質上定義了一個額外的時間窗口,在此期間緩存可使用舊的靜態資源,只要它在異步地在後臺從新驗證本身。所以,它「隱藏了」來自客戶端的延遲(在網絡和服務器上)。
在 2018 年 10 月,Chrome 發佈了一個意圖 在 HTTP Cache-Control 標頭中對 stale-while-revalidate
的處理,所以,它應該會改善後續頁面加載延遲,由於舊的靜態文件再也不位於關鍵路徑中。結果:重複訪問頁面的 RTT 爲零。
你可使用 Heroku 的 HTTP 緩存標頭入門,Jake Archibald 的「緩存最佳實踐」和Ilya Grigorik 的 HTTP 緩存入門做爲指南。另外,要注意標頭的變化,特別是與 CDN 相關的標頭,並注意 Key 標頭,這有助於避免當新請求與先前請求略有差別(但不顯着)時,須要進行額外的往返驗證(感謝 Guy!)。
另外,請仔細檢查你是否發送了沒必要要的標頭(例如 x-powered-by
、pragma
、x-ua-compatible
、expires
等),而且包含有用的安全性和性能標頭(例如 Content-Security-Policy
, X-XSS-Protection
, X-Content-Type-Options
等)。最後,請記住單頁應用程序中 CORS 請求的性能成本。
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。