改善整個 JavaScript 生態系統中的最大內容繪製。
在 Aurora 專案中,Google 一直與熱門的網路架構合作,確保這些架構能根據Core Web Vitals 的規定順利運作。Angular 和 Next.js 均已放入字型內嵌,詳情請參閱本文第一部分。我們將介紹的第二項最佳化方式是 CSS 內嵌,現在 Angular CLI 會預設啟用這項功能,並在 Nuxt.js 中實作中。
字型內嵌
在分析數百個應用程式後,Aurora 團隊發現開發人員經常在應用程式中加入字型,方法是在 index.html
的 <head>
元素中參照字型。以下是加入 Material Icons 後的範例:
<!doctype html>
<html lang="en">
<head>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
...
</html>
雖然這個模式完全有效且可正常運作,但會封鎖應用程式的算繪,並引入額外的要求。為進一步瞭解發生的情況,請查看上述 HTML 中參照的樣式表原始碼:
/* fallback */
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/font.woff2) format('woff2');
}
.material-icons {
/*...*/
}
請注意,font-face
定義如何參照 fonts.gstatic.com
上代管的外部檔案。載入應用程式時,瀏覽器必須先下載在標頭中參照的原始樣式表單。
接著,瀏覽器會下載 woff2
檔案,然後最終就能繼續轉譯應用程式。
最佳化機會是下載初始樣式表,並在 index.html
中內嵌。這樣一來,執行階段就不會再進行整個往返 CDN 的作業,進而縮短封鎖時間。
建構應用程式時,要求會傳送至 CDN,這會擷取樣式表並內嵌於 HTML 檔案,進而將 <link rel=preconnect>
新增至網域。套用這項技巧後,我們會得到以下結果:
<!doctype html>
<html lang="en">
<head>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin >
<style type="text/css">
@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/font.woff2) format('woff2');}.material-icons{/*...*/}</style>
...
</html>
字型內嵌功能現已可在 Next.js 和 Angular 中使用
當架構開發人員在基礎工具中實作最佳化功能時,現有和新應用程式就能更輕鬆地啟用這項功能,進而改善整個生態系統。
根據預設,Next.js 10.2 和 Angular 11 會啟用這項改善功能。兩者都支援內嵌 Google 和 Adobe 字型。Angular 預計會在 12.2 版中推出後者。
您可以在 GitHub 上找到 Next.js 中的字型內嵌實作,並查看這部影片,瞭解 Angular 中這項最佳化功能的用法。
內嵌重要 CSS
另一項改善措施是透過內嵌重要 CSS,改善首次顯示內容所需時間 (FCP) 和最大內容繪製 (LCP) 指標。網頁的關鍵 CSS 包含在初始轉譯時使用的所有樣式。如要進一步瞭解這個主題,請參閱「延後非必要的 CSS」。
我們發現許多應用程式會同步載入樣式,導致應用程式無法順利轉譯。快速解決方法是使用非同步方式載入樣式。請不要使用 media="all"
載入指令碼,而是將 media
屬性的值設為 print
,然後在載入完成後,將屬性值替換為 all
:
<link rel="stylesheet" href="..." media="print" onload="this.media='all'">
不過,這種做法可能導致沒有設定樣式的內容閃爍。
上方的影片顯示網頁的算繪作業,該作業會以非同步方式載入樣式。由於瀏覽器會先下載樣式,然後轉譯後續的 HTML,因此會出現閃爍現象。瀏覽器下載樣式後,就會觸發連結元素的 onload
事件,將 media
屬性更新為 all
,並將樣式套用至 DOM。
在轉譯 HTML 和套用樣式之間,網頁的部分樣式會未套用。當瀏覽器使用這些樣式時,我們會看到閃爍現象,這會導致使用者體驗不佳,並導致 累積版面配置位移 (CLS) 的回歸。
使用關鍵的 CSS 內嵌以及非同步樣式載入,都能改善載入行為。critters 工具會查看樣式表中的選取器,並將其與 HTML 進行比對,以找出網頁使用的樣式。 找到相符項目後,系統會將對應的樣式視為必要 CSS 的一部分,並將其內嵌。
讓我們看看以下範例:
<head> <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'"> </head> <body> <section> <button class="primary"></button> </section> </body>
/* styles.css */ section button.primary { /* ... */ } .list { /* ... */ }
在上例中,Critters 會讀取並剖析 styles.css
的內容,接著將兩個選取器與 HTML 比對,並發現我們使用 section button.primary
。最後,Critters 會在頁面的 <head>
中內嵌對應的樣式,產生以下結果:
<head> <link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'"> <style> section button.primary { /* ... */ } </style> </head> <body> <section> <button class="primary"></button> </section> </body>
在 HTML 中內嵌重要 CSS 後,您會發現網頁的閃爍現象已消失:
您現在可以在 Angular 中使用 Critical CSS 內嵌功能,且在 v12 中預設為啟用。如果您使用的是 v11,請在 angular.json
中將 inlineCritical
屬性設為 true
,如要在 Next.js 中啟用這項功能,請將 experimental: { optimizeCss: true }
新增至 next.config.js
。
結論
在這篇文章中,我們談到一些 Chrome 與網路架構之間的合作。如果您是架構作者,並認同我們在技術中解決的部分問題,希望我們的發現能激勵您採用類似的效能最佳化方式。
進一步瞭解改善項目。如要查看我們為 Core Web Vitals 進行的完整最佳化工作,請參閱「Aurora 簡介」一文。