在 2018 年 Google IO 大會上,我們發表了一系列可提高網路效能的工具、程式庫和最佳化技術。我們將透過 Oodles Theater 應用程式說明這些概念,並討論我們在預測載入和新版 Guess.js 計畫方面的實驗。
過去一年,我們一直忙於研究如何加快網站速度並提升效能。因此,我們在本文中提供新的工具、方法和程式庫。在第一部分,我們會說明在開發 The Oodles Theater 應用程式時,我們實際採用的部分最佳化技巧。在第二部分,我們會說明我們在預測載入和新的 Guess.js 計畫中進行的實驗。
效能需求
網際網路的負擔逐年增加,查看網路狀態,我們可以發現行動版網頁的平均大小約為 1.5 MB,其中大部分是 JavaScript 和圖片。
隨著網站規模不斷增加,加上網路延遲、CPU 限制、轉譯模式或大量第三方程式碼等其他因素,也會導致複雜的效能問題發生。
大多數使用者都認為速度是使用者體驗層級中最重要的需求。這並非令人意外,因為網頁載入完成前,您無法執行太多操作。您無法從頁面中獲得價值,也無法欣賞其美學。
我們知道效能對使用者來說很重要,但也可能讓人覺得可以在哪裡著手最佳化。幸運的是,我們有工具可以協助你完成這項工作。
Lighthouse - 效能工作流程的基礎
Lighthouse 是 Chrome 開發人員工具的一部分,可讓您稽核網站,並提供改善網站的提示。
我們最近推出了一系列新的效能稽核,這些稽核工具在日常開發工作流程中非常實用。
讓我們透過實際範例探索如何運用這些功能:Oodles Theater 應用程式。這是一個小型示範網頁應用程式,您可以試用我們最喜愛的互動式 Google Doodle,甚至可以玩一兩款遊戲。
在建構應用程式時,我們希望確保應用程式盡可能提供最佳效能。我們從 Lighthouse 報表開始進行最佳化。
Lighthouse 報告指出,我們應用程式的初步效能相當嚴重。在 3G 網路上,使用者必須等待 15 秒才能看到第一個有意義的繪圖,或等到應用程式可供互動。Lighthouse 在我們的網站中突顯出許多問題,整體效能分數 (23) 也反映出相同情況。
網頁的權重約為 3.4 MB,我們需要剪掉一些脂肪。
這就是我們遇到的第一個效能挑戰:找出可以輕易移除的項目,而不影響整體體驗。
效能最佳化商機
移除不必要的資源
有些明顯的項目可以安全移除,例如空格和註解。
Lighthouse 會在 未經壓縮的 CSS 和 JavaScript 稽核中強調這項機會。我們先前使用 webpack 進行建構,因此只要使用 Uglify JS 外掛程式即可進行壓縮。
縮減是常見的工作,因此您應該可以找到適合所用建構程序的現成解決方案。
這個領域的另一項實用稽核是啟用文字壓縮。您沒有理由傳送未壓縮的檔案,而且大多數的 CDN 在現今都支援這項功能。
我們使用 Firebase 代管服務代管程式碼,而 Firebase 預設會啟用 gzip 壓縮,因此只要在合理的 CDN 上代管程式碼,我們就能免費獲得這項功能。
雖然 gzip 是相當熱門的壓縮方式,但Zopfli 和 Brotli 等其他機制也開始受到重視。Brotli 可在大多數瀏覽器中使用,您可以在傳送至伺服器前,使用二進位檔案預先壓縮素材資源。
使用有效的快取政策
下一步是確保在沒有必要的情況下,不會重複傳送資源。
Lighthouse 的快取政策效率低落稽核結果顯示,如要達到這個目標,我們可以對快取策略進行最佳化調整。在伺服器中設定 max-age 到期標頭,可確保使用者在重複造訪時,可以重複使用先前下載的資源。
在理想情況下,您應盡可能在最短時間內以安全的方式快取最多資源,並提供驗證憑證,以便有效率地重新驗證已更新的資源。
移除未使用的程式碼
到目前為止,我們已移除不必要下載作業的明顯部分,但不明顯的部分呢?例如未使用的程式碼。
有時我們會在應用程式程式碼中加入不必要的內容。特別是在您長時間處理應用程式、團隊或依附元件發生變更,以及有時候遺留孤立的程式庫時,就會發生這種情況。我們就是這樣。
首先我們使用 Material Design 元件程式庫,快速設計應用程式的原型。當時,我們改用更自訂的外觀與風格,因此完全忘記該程式庫。幸運的是,程式碼涵蓋率檢查功能協助我們在套件中重新找出這項問題。
您可以在 DevTools 中查看程式碼涵蓋率統計資料,包括應用程式的執行階段和載入時間。您可以在底部的螢幕截圖中看到兩條大紅條紋,這代表我們有超過 95% 的 CSS 未使用,以及大量的 JavaScript。
Lighthouse 在未使用的 CSS 規則稽核中也發現這個問題。顯示可節省超過 400kb。因此,我們回到程式碼,並移除了該程式庫的 JavaScript 和 CSS 部分。
這讓 CSS 套件縮小了 20 倍,對於只有兩行程式碼的小型提交來說,這已相當不錯。
當然,這也讓我們的成效分數提升,互動時間也大幅改善。
不過,在這種情況下,光是查看指標和分數是不夠的。移除實際程式碼並非零風險,因此請務必留意潛在的迴歸問題。
我們的程式碼在 95% 的情況下都未使用,但仍有 5% 的情況會使用。其中一個元件仍使用該程式庫的樣式,也就是塗鴉滑桿中的小箭頭。不過,由於它很小,我們可以手動將這些樣式重新整合到按鈕中。
因此,如果您移除程式碼,請務必確保已建立適當的測試工作流程,以防範潛在的視覺回歸現象。
避免耗用大量網路酬載
我們知道大型資源可能會拖慢網頁載入速度。這類內容可能會讓使用者付費,並對他們的資料方案造成重大影響,因此請務必留意這點。
Lighthouse 使用龐大的網路酬載稽核功能,偵測到我們在某些網路酬載上遇到問題。
這裡我們看到我們捨棄了超過 3 MB 的程式碼,這已大不相同,特別是在行動裝置上。
這份清單最頂端強調我們有一個 JavaScript 廠商組合,也就是未壓縮程式碼的 2 MB。這也是 webpack 強調的問題。
如同俗諺所言:「最快的請求,就是不提出要求。」
理想情況下,您應該評估向使用者提供的每項素材資源的價值,評估這些素材資源的效能,並判斷是否值得將初始體驗提供給使用者。因為有時這些素材資源可能會在閒置期間延遲、延遲載入或進行處理。
在這個案例中,由於我們必須處理許多 JavaScript 套件,所以好消息是由於 JavaScript 社群提供一套豐富的 JavaScript 組合稽核工具。
我們一開始先從 webpack 組合分析工具開始,指出我們加入了名為 unicode 的依附元件,這個依附元件是 1.6 MB 的剖析 JavaScript,因此好很多。
接著前往我們的編輯器,使用視覺化程式碼匯入費用外掛程式,就能以視覺化的方式呈現每個匯入模組的費用。這讓我們能夠找出哪個元件包含參照此模組的程式碼。
接著,我們改用另一個工具 BundlePhobia。這項工具可讓您輸入任何 NPM 套件的名稱,並實際查看其壓縮和 gzip 大小的估計值。我們找到了一個不錯的替代方案,可取代我們使用的 slug 模組,而且檔案大小只有 2.2kb,因此我們改用這個方案。
這對我們的成效造成重大影響。在進行這項變更及發現其他可縮減 JavaScript 套件大小的機會之間,我們節省了 2.1 MB 的程式碼。
將套件中的 gzip 和壓縮大小納入考量後,我們發現整體效能提升了 65%。 我們發現這項程序確實值得採用。
因此,一般來說,請盡量避免在網站和應用程式中提供不必要的下載項目。清點資產並衡量其效能影響,可能會產生巨大影響,因此務必定期稽核資產。
透過程式碼分割縮短 JavaScript 啟動時間
雖然大型網路酬載可能對應用程式造成重大影響,但 JavaScript 也是另一個可能造成重大影響的因素。
JavaScript 是最昂貴的資產。在行動裝置上,如果您傳送大量的 JavaScript 套件,可能會延遲使用者與使用者介面元件互動的時間。也就是說,使用者可以輕觸 UI,但系統不會實際執行任何有意義的動作。因此,我們必須瞭解 JavaScript 的成本為何如此高。
瀏覽器處理 JavaScript 的方式就是這樣。
我們首先必須下載該指令碼,我們有一個 JavaScript 引擎,需要剖析該程式碼,需要編譯並執行該程式碼。
在高階裝置 (例如桌上型電腦或筆記型電腦,甚至是高階手機) 上,這些階段不會耗費太多時間。但在中位數手機上,此程序可能需要五到十倍的時間。這是互動延遲的情況 因此我們必須設法移除這個延遲
為協助您找出應用程式中的這些問題,我們在 Lighthouse 中推出了新的 JavaScript 啟動時間稽核。
以 Oodle 應用程式為例,這項工具指出 JavaScript 啟動作業耗時 1.8 秒。我們將所有路徑和元件靜態匯入至單一 JavaScript 套件。
解決這個問題的方法之一,就是使用程式碼分割。
程式碼分割是這樣的概念,而不是給予使用者一整份披薩價值的 JavaScript。若只在需要的時候一次提供一個切片呢?
您可以在路徑層級或元件層級套用程式碼分割。這個程式庫非常適合 React 和 React Loadable、Vue.js、Angular、Polymer、Preact 和其他多個程式庫使用。
我們將程式碼分割作業納入應用程式,並從靜態匯入切換為動態匯入,以便在需要時異步延後載入程式碼。
主要影響在於縮減套件大小,同時也會縮短 JavaScript 的啟動時間。這項作業只花了 0.78 秒,讓應用程式加快 56%。
一般來說,如果您要建構大量使用 JavaScript 的體驗,請務必只將需要的程式碼傳送給使用者。
善用程式碼分割等概念、探索樹狀圖抖動等構想,並查看 webpack-libs-optimizations 存放區,瞭解如何縮減程式庫大小 (如果您正好使用 webpack 的話)。
最佳化圖片
在 Oodle 應用程式中,我們使用了許多圖片。很遺憾,Lighthouse 對此的熱情遠不及我們。事實上,我們無法完成全部三項圖片相關稽核作業。
我們忘了對圖片進行最佳化,也沒有正確調整圖片大小,而且我們可以透過使用其他圖片格式獲得一些優勢。
我們首先從圖片最佳化著手。
如要進行一次性的最佳化,您可以使用 ImageOptim 或 XNConvert 等視覺化工具。
更自動化的方法是在建構程序中加入圖片最佳化步驟,並使用 imagemin 等程式庫。
這樣一來,日後新增的圖片就會自動進行最佳化處理。有些 CDN (例如 Akamai) 或第三方解決方案 (例如 Cloudinary、Fastly 或 Uploadcare) 提供全面的圖片最佳化解決方案,因此您也可以直接在這些服務上代管圖片。
如果您不想因為成本或延遲問題而不想這麼做,例如 Thumbor 或 Imageflow 等專案會提供自行託管的替代方案。
我們的背景 PNG 在 webpack 中被標示為太大,這點沒錯。在正確調整大小以符合可視區域,並透過 ImageOptim 執行後,我們將大小降至 100kb,這已是可接受的大小。
我們在網站上重複執行這項操作,因此大幅降低了整體網頁重量。
使用適當的動畫內容格式
GIF 成本非常高昂。令人驚訝的是,GIF 格式從來就不是用來做為動畫平台。因此,改用更合適的影片格式,可大幅縮減檔案大小。
在 Oodle 應用程式中,我們使用 GIF 做為首頁上的簡介序列。根據 Lighthouse 的資料,如果改用更有效率的影片格式,可以節省超過 7 MB。我們的短片檔案大小約為 7.3 mb,對於任何合理的網站來說都太大了,因此我們將其轉換為包含兩個來源檔案的影片元素,分別為 mp4 和 WebM,以便支援更多瀏覽器。
我們使用 FFmpeg 工具將動畫 GIF 轉換成 mp4 檔案。WebM 格式可讓你節省更多空間 - ImageOptim API 可為你執行這類轉換。
ffmpeg -i animation.gif -b:v 0 -crf 40 -vf scale=600:-1 video.mp4
經過這次轉換,我們得以省下超過 80% 的總體重量。這讓我們將大小縮減到約 1 mb。
不過,1 MB 仍是透過網路傳輸的大量資源,尤其是對於頻寬受限的使用者而言。幸運的是,我們可以使用 Effective Type API 判斷使用者連線速度緩慢,並提供較小的 JPEG 檔案。
這個介面會使用有效的往返時間和值來估算使用者正在使用的網路類型。它只會傳回字串、慢速 2G、2G、3G 或 4G。因此,視這個值而定,如果使用者使用的是 4G 以下網路,我們可以將影片元素替換成圖片。
if (navigator.connection.effectiveType) { ... }
這麼做會讓使用者感覺不到一點,但網站在連線速度緩慢的情況下仍然能夠使用。
延遲載入畫面外圖片
輪轉介面、滑桿或超長網頁通常會載入圖片,即使使用者無法立即在網頁上看到這些圖片。
Lighthouse 會在離螢幕圖片稽核中標示這項行為,您也可以在開發人員工具的網路面板中查看。如果您發現大量圖片正在傳入,但只有少數圖片顯示在頁面上,表示您可能可以改為延後載入圖片。
瀏覽器尚未原生支援延後載入功能,因此我們必須使用 JavaScript 新增這項功能。我們使用 Lazysizes 程式庫,為 Oodle 封面新增延遲載入行為。
<!-- Import library -->
import lazysizes from 'lazysizes' <!-- or -->
<script src="lazysizes.min.js"></script>
<!-- Use it -->
<img data-src="image.jpg" class="lazyload"/>
<img class="lazyload"
data-sizes="auto"
data-src="image2.jpg"
data-srcset="image1.jpg 300w,
image2.jpg 600w,
image3.jpg 900w"/>
Lazysize 不但會追蹤元素的可見度變更,還會主動預先擷取位於檢視畫面附近的元素,提供最佳使用者體驗。它也提供 IntersectionObserver
的選用整合功能,可讓您非常有效率地查看曝光率。
這項異動後,我們會視需要擷取圖片。如要進一步瞭解這個主題,請參閱 images.guide 這項實用且全面的資源。
協助瀏覽器提早提供重要資源
並非所有傳送至瀏覽器的位元組都具有相同的重要性,瀏覽器也知道這一點。許多瀏覽器都會使用啟發式搜尋來決定應優先擷取哪些內容。因此有時會先擷取 CSS,再擷取圖片或指令碼。
我們可以做的實用之處,就是在網頁作者身分下,告知瀏覽器對我們來說真正重要的內容。幸好,過去幾年來,瀏覽器廠商不斷為我們添加了許多功能,例如 link rel=preconnect
、preload
或 prefetch
等資源提示。
這些功能已導入網頁平台,可協助瀏覽器在適當時間擷取正確內容,而且比起使用指令碼執行的部分自訂載入邏輯方法,這些功能的效率稍微高一些。
讓我們來看看 Lighthouse 如何引導我們有效運用這些功能。
Lighthouse 首先建議我們避免多次往返任何來源的費用。
以 Oodle 應用程式為例,我們實際上大量使用 Google Fonts。當您將 Google Font 樣式表加到網頁中時,它最多可以連結兩個子網域。Lighthouse 指出,如果我們能夠預熱該連線,就能在初始連線時間節省最多 300 毫秒。
我們可以利用 rel 預先連結連結,有效地掩蓋連線延遲時間。
特別是像 Google Fonts 這類字型 CSS 是由 googleapis.com 代管,而字型資源由 Gstatic 代管時,這會造成重大影響。因此我們套用了這項最佳化方式,節省了幾百毫秒的時間。
接著 Lighthouse 建議要預先載入主要要求。
<link rel=preload>
非常強大,它會通知瀏覽器需要的資源,做為目前導覽的一部分,並嘗試讓瀏覽器盡快擷取資源。
這裡 Lighthouse 會告訴我們,我們應該預先載入主要的網路字型資源,因為我們正在載入兩種網路字型。
在網路字型中預先載入的情況如下所示:指定 rel=preload
,您會傳入 as
並指定字型的類型,然後指定要載入的字型類型,例如 woff2。
這可能會對你的網頁造成嚴重影響。
通常,如果網頁需要使用網頁字型,但未使用 rel 連結預先載入,瀏覽器就必須先擷取 HTML,再剖析 CSS,最後才會擷取網頁字型。
使用 link rel preload 後,瀏覽器在剖析 HTML 後,就能提早開始擷取這些網路字型。就我們的應用程式而言,這大幅縮短了使用網頁字型轉譯文字的時間。
不過,如果您想嘗試使用 Google Fonts 預先載入字型,則必須留意以下事項。
我們在樣式表的字型上指定了 Google Font 網址,是字型團隊定期更新的問題。這些網址可能會過期,或定期更新。如果您想完全掌控字型載入體驗,建議您自行代管網頁字型。這個方法很實用,因為您可以存取連結 rel 預先載入等項目
在我們的案例中,我們發現 Google Web Fonts Helper 工具非常實用,可協助我們離線下載部分網頁字型並在本機設定,因此請試試這個工具。
無論您是將網路字型當作重要資源的一部分,還是使用 JavaScript,請盡可能協助瀏覽器盡快提供重要資源。
實驗功能:優先順序提示
今天我們要分享一些特別的內容,除了資源提示和預先載入等功能之外,我們也一直在開發一項全新的實驗性瀏覽器功能,稱為「優先順序提示」。
這是一項新功能,可讓您向瀏覽器提示資源的重要性。這項功能會公開一個新的屬性 - importance - 其值為 low、high 或 auto。
這可讓我們降低非必要資源的優先順序,例如非必要的樣式、圖片或擷取 API 呼叫,以減少競爭。我們也可以提高更重要內容的優先順序,例如主圖片。
以 Oodle 應用程式為例,這項做法實際上讓我們找到了一個可實現最佳化的地方。
在為圖片加上延遲載入之前,我們先可以看到這個圖片輪轉介面和所有 Doodle,而瀏覽器在輪轉介面一開始就以高優先順序擷取所有圖片。很遺憾,輪轉介面中間的圖片對使用者而言最為重要。因此,我們將背景圖片的重要性設為非常低,而將前景圖片的重要性設為非常高,這會在 3G 慢速網路上造成兩秒的影響,而我們擷取及算繪這些圖片的速度也因此有所影響。所以,這項服務的體驗相當不錯。
我們希望在幾週內將這項功能帶入 Canary,敬請密切留意。
制定網站字型載入策略
排版是優質設計的基礎,如果您使用的是網路字型,建議不要阻擋文字的算繪作業,也不要顯示不可見的文字。
我們現在會在 Lighthouse 中強調這一點,並避免在載入網路字型時避免隱藏文字。
如果您使用字型樣式區塊載入網路字型,當網路字型擷取作業耗費很長的時間時,瀏覽器會決定要採取哪種行動。有些瀏覽器會等待任何時間長達三秒,才會改回使用系統字型,而瀏覽器在下載完畢後,最終會將它切換為該字型。
我們正在努力避免這類隱形文字,所以如果網路字型使用過長,我們就無法看到本週的經典 Doodle。幸好,您可以透過名為 font-display
的新功能,進一步控管這項程序。
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-display: swap;
font-weight: 400;
src: local('Montserrat Regular'), local('Montserrat-Regular'),
/* Chrome 26+, Opera 23+, Firefox 39+ */
url('montserrat-v12-latin-regular.woff2') format('woff2'),
/* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
url('montserrat-v12-latin-regular.woff') format('woff');
}
字型顯示可根據文字的替換所需時間,協助您決定網路字型的算繪或備用方式。
在本例中,我們使用字型顯示交換功能。換字會為字體面提供零秒的區塊時間間隔和無限的換字時間間隔。這表示如果字型需要一段時間載入,瀏覽器就會立即使用備用字型來繪製文字。而且字型一旦可供使用
在我們的應用程式中,這麼做很棒的原因在於,我們可以盡早顯示一些有意義的文字,並在準備就緒後切換至網頁字型。
一般來說,如果您使用的是網路字型 (網路上有大量使用這類字型),請採用良好的網路字型載入策略。
您可以使用許多網站平台功能,改善字型載入體驗,但也請查看 Zach Leatherman 的 Web Font Recipes 存放區,因為這個存放區非常實用。
減少會妨礙顯示的指令碼
我們可以提早推送應用程式的其他部分,在下載鏈結中提前推送,至少稍微提供一些基本的使用者體驗。
在 Lighthouse 時間軸列上,您可以看到在前幾秒內,當所有資源載入時,使用者實際上無法看到任何內容。
下載及處理外部樣式表會阻斷轉譯程序,導致轉譯程序無法取得任何進展。
我們可以稍微提前傳送一些樣式,嘗試最佳化重要轉譯路徑。
如果我們擷取負責初始轉譯作業的樣式,並在 HTML 中內嵌這些樣式,瀏覽器就能立即轉譯這些樣式,而無須等待外部樣式表到達。
在本例中,我們使用名為 Critical 的 NPM 模組,在建構步驟期間在 index.html 中內嵌關鍵內容。
儘管本單元已確實完成大部分的繁重工作,但仍有點棘手,才能在不同的路線上順暢運作。
如果您不小心,或是網站結構非常複雜,而您一開始並未規劃應用程式殼結構,那麼導入這類模式可能會非常困難。
因此,盡早考量效能至關重要。如果您一開始就沒有考量效能,日後很可能會遇到問題。
最後,我們承認風險後,成功將成果付諸實現,應用程式也更早開始提供內容,大幅改善了第一次有意義的繪製時間。
成果
這是我們在網站上套用的一長串效能最佳化措施。讓我們來看看結果。這就是應用程式在經過 3G 網路最佳化前後,在中等行動裝置上載入的方式。
Lighthouse 效能分數從 23 提高到 91。就速度而言,這算是相當不錯的進展。我們持續檢查並遵循 Lighthouse 報告的建議,因此做出了所有變更。如要瞭解我們如何在技術層面實作所有改善項目,歡迎參閱我們的存放區,尤其是其中的 PR。
預測成效:以資料為依據的使用者體驗
我們相信機器學習對許多領域未來的發展來說,是絕佳的契機。 我們希望這項概念能激發更多實驗,讓我們在日後的創作過程中,能以真實資料為依據來設計使用者體驗。
時至今日,我們經常根據使用者的需求或需求做出許多決定,因此哪些資料值得預先擷取、預先載入或預先快取。如果我們猜對了,就能將資源優先分配給少數人,但要將其擴大到整個網站,就很難了。
我們現在有資料可供參考,以便進一步改善最佳化成效。使用 Google Analytics 報表 API,我們可以查看網站上任何網址的下一個熱門網頁和離開百分比,進而得出結論,決定應優先使用哪些資源。
如果我們將這項功能與良好的機率模型結合,就能避免因過度預先擷取內容而浪費使用者資料。我們可以利用 Google Analytics 資料,並使用機器學習和模型 (例如 馬可夫鏈或神經網路) 來實作這類模型。
為了協助進行這項實驗,我們很高興在此宣布推出名為 Guess.js 的新計畫。
Guess.js 是一項專案,著重於資料導向的網路使用者體驗。我們希望這項功能能激勵您探索如何運用資料改善網站效能,並進一步發揮效益。這項工具為開放原始碼,現已在 GitHub 上架。這項工具是由 Minko Gechev、Gatsby 的 Kyle Matthews、Katie Hempenius 和其他許多人與開放原始碼社群合作開發。
快來試試 Guess.js,與我們分享您的想法。
摘要
分數和指標僅是改善網頁速度的參考依據,並非目標本身。
我們都曾經歷過網頁載入速度緩慢的情況,但現在我們有這個機會,為使用者提供更快速的載入體驗。
提升效能是一條漫長的旅程。許多小改變累積起來就能帶來重大成效。只要使用合適的最佳化工具,並密切留意 Lighthouse 報表,即可為使用者提供更優質且更多元包容的體驗。
特別感謝:Ward Peeters、Minko Gechev、Kyle Mathews、Katie Hempenius、Dom Farolino、Yoav Weiss、Susie Lu、Yusuke Utsunomiya、Tom Ankers、Lighthouse 和 Google Doodles。