讓我們一起聊聊如何提高 CSS 性能?
結(jié)合現(xiàn)代網(wǎng)站的復(fù)雜性和瀏覽器處理 CSS 的方式,即使是適量的 CSS 也可能成為處理受限設(shè)備、網(wǎng)絡(luò)延遲、帶寬或數(shù)據(jù)限制的人的瓶頸。
由于性能是用戶(hù)體驗(yàn)的重要組成部分,因此必須確??绺鞣N形狀和大小的設(shè)備提供一致、高質(zhì)量的體驗(yàn),這也需要優(yōu)化 CSS。
這篇文章將介紹 CSS 會(huì)導(dǎo)致哪些類(lèi)型的性能問(wèn)題,以及制作不會(huì)妨礙人們的 CSS 的最佳實(shí)踐。
一、CSS 是如何工作的?
1.CSS 塊渲染
當(dāng)頁(yè)面有可用的 CSS 時(shí),無(wú)論是內(nèi)聯(lián)樣式表還是外部樣式表,瀏覽器都會(huì)延遲呈現(xiàn),直到 CSS 被解析。這是因?yàn)闆](méi)有 CSS 的頁(yè)面通常無(wú)法使用。
如果瀏覽器向你展示了一個(gè)沒(méi)有 CSS 的凌亂頁(yè)面,那么片刻之后就會(huì)進(jìn)入一個(gè)有樣式的頁(yè)面,不斷變化的內(nèi)容和突然的視覺(jué)變化會(huì)讓用戶(hù)體驗(yàn)變得混亂。糟糕的用戶(hù)體驗(yàn)有一個(gè)名字——無(wú)樣式內(nèi)容閃現(xiàn)(FOUC)
2.CSS 可以阻止 HTML 解析
盡管瀏覽器在解析完 CSS 之前不會(huì)顯示內(nèi)容,但它會(huì)處理 HTML 的其余部分。但是,腳本會(huì)阻止解析器,除非它們被標(biāo)記為defer或async。腳本可能會(huì)操縱頁(yè)面和其余代碼,因此瀏覽器必須注意該腳本何時(shí)執(zhí)行。
解析器阻塞腳本:腳本如何阻塞 HTML 解析。
因?yàn)槟_本會(huì)影響應(yīng)用到頁(yè)面的樣式,如果瀏覽器仍在處理一些 CSS,它會(huì)等到它完成后再運(yùn)行腳本。由于在腳本運(yùn)行之前它不會(huì)繼續(xù)解析文檔,這意味著 CSS 不再只是阻止渲染——根據(jù)外部樣式表的順序,文檔中的腳本也可能會(huì)停止 HTML 解析。
Parser Blocking CSS:CSS 如何阻止 HTML 解析。
為避免阻塞解析,請(qǐng)盡快交付 CSS 并以最佳順序安排您的資源。
二、觀(guān)察 CSS 的大小
1.壓縮和縮小 CSS
建立連接去下載外部樣式表不可避免地會(huì)導(dǎo)致延遲,但您可以通過(guò)最小化通過(guò)網(wǎng)絡(luò)傳輸?shù)目傋止?jié)數(shù)來(lái)加快下載速度。
壓縮文件可以顯著提高速度,許多托管平臺(tái)和 CDN 默認(rèn)使用壓縮編碼資產(chǎn)(或者您可以輕松配置它們)。服務(wù)器和客戶(hù)端交互使用最廣泛的壓縮格式是Gzip。還有Brotli可以提供更好的壓縮結(jié)果,盡管它不像 Gzip 那樣受支持。
縮小CSS是刪除空格和任何不需要的代碼的過(guò)程。輸出是一個(gè)較小但完全有效的代碼文件,瀏覽器可以解析它,這將為您節(jié)省一些字節(jié)。Terser是一種流行的 JavaScript 壓縮工具,或使用webpack,v4 插件來(lái)創(chuàng)建縮小的構(gòu)建文件。
2.刪除未使用的 CSS
使用 CSS 框架時(shí),以未使用的 CSS 結(jié)束是比較常見(jiàn)的(除非我們只包含我們需要的組件)。同樣的問(wèn)題出現(xiàn)在長(zhǎng)期增長(zhǎng)的大型代碼庫(kù)中。
刪除未使用的 CSS 通常是手動(dòng)工作。主要的挑戰(zhàn)是它有多復(fù)雜。我們必須在所有可能的狀態(tài)下,在所有可能的設(shè)備(涵蓋媒體查詢(xún))上仔細(xì)審核整個(gè)站點(diǎn),并執(zhí)行所有可能改變樣式的 JavaScript 功能。
UnusedCSS 和 PurifyCSS 是可以幫助確定不必要樣式的流行工具,但我們應(yīng)該將它們與仔細(xì)的視覺(jué)回歸測(cè)試配對(duì)。
這就是使用 CSS-in-JS 的顯著優(yōu)勢(shì):每個(gè)組件中呈現(xiàn)的樣式僅需要 CSS??焖?CSS-in-JS 的秘訣是將 CSS 內(nèi)聯(lián)到頁(yè)面中或?qū)⑵涮崛〉酵獠? CSS 文件中。在 JavaScript 文件中傳送 CSS 將導(dǎo)致它被解析和評(píng)估緩慢。
三、優(yōu)先考慮關(guān)鍵 CSS
關(guān)鍵CSS是一種為首屏內(nèi)容提取和內(nèi)聯(lián)CSS的技術(shù)。在HTML文檔的中內(nèi)聯(lián)提取的樣式,無(wú)需發(fā)出額外的請(qǐng)求來(lái)獲取這些樣式并加快渲染速度。
為了最大限度地減少首次渲染的往返次數(shù),請(qǐng)將首屏內(nèi)容保持在14 KB(壓縮)以下。
確定關(guān)鍵 CSS 并不完全準(zhǔn)確,因?yàn)槟枰獙?duì)折疊位置(因設(shè)備屏幕尺寸而異)做出假設(shè)。這對(duì)于高度動(dòng)態(tài)的站點(diǎn)來(lái)說(shuō)可能很困難。即使不精確,它仍然可以帶來(lái)性能改進(jìn),我們可以使用Critical、CriticalCSS和Penthouse等工具將其自動(dòng)化。
1.異步加載 CSS
CSS的其余部分(不太重要的部分)最好異步加載。實(shí)現(xiàn)這一點(diǎn)的方法是將鏈接媒體屬性設(shè)置為print:
“打印”媒體類(lèi)型定義了用戶(hù)嘗試打印頁(yè)面時(shí)的樣式表規(guī)則,瀏覽器將加載此類(lèi)樣式表而不會(huì)延遲頁(yè)面渲染。將該樣式表應(yīng)用于所有媒體(即屏幕而不僅僅是打印)使用onload屬性在樣式表完成加載時(shí)將媒體設(shè)置為全部。
另一種選擇是使用<link rel="preload">(而不是rel="stylesheet")來(lái)實(shí)現(xiàn)與上述類(lèi)似的模式,并在加載事件時(shí)將rel屬性切換到樣式表。使用這種方法時(shí)需要考慮一些缺點(diǎn)。
· 瀏覽器對(duì)預(yù)加載的支持仍然不是很好,因此需要一個(gè) polyfill(或使用諸如loadCSS 之類(lèi)的庫(kù))來(lái)跨瀏覽器應(yīng)用樣式表。
· 預(yù)加載很早就以最高優(yōu)先級(jí)獲取文件,可能會(huì)降低其他重要下載的優(yōu)先級(jí)。
如果你確實(shí)想要preload提供的高優(yōu)先級(jí)獲取(在支持它的瀏覽器中),loadCSS 的創(chuàng)建者建議你將它與第一個(gè)模式結(jié)合起來(lái),像這樣:
2.避免在 CSS 文件中使用 @import
在 CSS 文件中使用@import會(huì)減慢渲染速度。首先,瀏覽器必須下載 CSS 文件以發(fā)現(xiàn)導(dǎo)入的資源,然后在渲染之前發(fā)起另一個(gè)下載請(qǐng)求。
如果你有一個(gè)包含@import url(imported.css) 的樣式表;網(wǎng)絡(luò)瀑布如下所示:
在鏈接元素中加載兩個(gè)樣式表允許并行下載:
四、使用高效的 CSS 動(dòng)畫(huà)
當(dāng)您為頁(yè)面上的元素設(shè)置動(dòng)畫(huà)時(shí),瀏覽器通常必須重新計(jì)算它們?cè)谖臋n中的位置和大小,這會(huì)觸發(fā)布局。例如,如果您更改元素的寬度,則其任何子元素都可能受到影響,并且頁(yè)面布局的很大一部分可能會(huì)更改。布局幾乎總是作用于整個(gè)文檔,所以布局樹(shù)越大,它執(zhí)行布局計(jì)算的時(shí)間就越長(zhǎng)。
為元素設(shè)置動(dòng)畫(huà)時(shí),必須盡量減少布局和重繪。并非所有 CSS 動(dòng)畫(huà)技術(shù)都是平等的,現(xiàn)代瀏覽器可以最好地創(chuàng)建具有位置、縮放、旋轉(zhuǎn)和不透明度的高性能動(dòng)畫(huà):
- 不要更改高度和寬度屬性,而是使用transform: scale()。
- ·四處移動(dòng)元素,請(qǐng)避免更改top、right、bottom或left屬性并使用transform: translate()代替。
- ·如果要模糊背景,請(qǐng)考慮使用模糊圖像并更改其不透明度。
在包含 CSS屬性告訴瀏覽器的元素及其后代被認(rèn)為是獨(dú)立于文檔樹(shù)(盡可能)。它將頁(yè)面的子樹(shù)與其余部分隔離開(kāi)來(lái)。然后瀏覽器可以?xún)?yōu)化頁(yè)面獨(dú)立部分的渲染(樣式、布局和繪制操作)以提高性能。
該包含屬性是在包含許多獨(dú)立的小部件頁(yè)面有用。我們可以使用它來(lái)防止每個(gè)小部件內(nèi)的更改在小部件的邊界框之外產(chǎn)生副作用。一個(gè)主要是靜態(tài)的站點(diǎn)不會(huì)從這個(gè)策略中獲得什么好處。
五、使用 CSS 優(yōu)化字體加載
1.在字體加載期間避免不可見(jiàn)的文本
字體通常是需要一段時(shí)間才能加載的大文件。一些瀏覽器會(huì)在字體加載之前隱藏文本(導(dǎo)致“不可見(jiàn)文本閃爍”或 FOIT)來(lái)解決這個(gè)問(wèn)題。在優(yōu)化速度時(shí),您需要避免“不可見(jiàn)文本閃爍”,并立即使用系統(tǒng)字體(一種預(yù)裝在他們機(jī)器上的字體)向人們顯示內(nèi)容。加載字體文件后,它將替換稱(chēng)為“無(wú)樣式文本閃爍”或 FOUT 的系統(tǒng)字體。
實(shí)現(xiàn)此目的的一種方法是使用front-display 一種用于指定字體顯示策略的API。使用帶有值swap的font-display告訴瀏覽器使用該字體的文本應(yīng)該立即使用系統(tǒng)字體顯示。
2.使用可變字體來(lái)減小文件大小
可變字體使字體的許多不同變體能夠合并到一個(gè)文件中,而不是為每個(gè)寬度、粗細(xì)或樣式都設(shè)置一個(gè)單獨(dú)的字體文件。它們?cè)试S您使用 CSS 和單個(gè)@font-face引用訪(fǎng)問(wèn)給定字體文件中的所有變體。
在需要多種字體變體的情況下,可變字體可以顯著減小文件大小。您可以加載包含所有信息的單個(gè)文件,而不是加載常規(guī)和粗體樣式及其斜體版本。
Monotype 進(jìn)行了一項(xiàng)實(shí)驗(yàn),通過(guò)組合 12 種輸入字體來(lái)生成 8 個(gè)權(quán)重,跨越三種寬度,跨越斜體和羅馬風(fēng)格。在單個(gè)可變字體文件中存儲(chǔ) 48 種獨(dú)立字體意味著文件大小減少了 88%。
六、不用擔(dān)心 CSS 選擇器的速度
CSS 選擇器的結(jié)構(gòu)會(huì)影響瀏覽器匹配它們的速度。瀏覽器從右到左讀取選擇器,因此當(dāng)您使用后代選擇器時(shí)。例如,nav a {},它將首先匹配頁(yè)面上的每個(gè)元素,然后在nav內(nèi)部的元素上歸零。如果您使用更具體的選擇器,例如,在 nav 元素內(nèi)的每個(gè)上使用.nav-link,它就不會(huì)花時(shí)間嘗試匹配頁(yè)面上的每個(gè)。
如果您考慮瀏覽器如何從右到左匹配選擇器以及.container ul li a { } 之類(lèi)的示例,您就會(huì)明白為什么后代選擇器通常被標(biāo)記為“重要”的原因。
這樣的選擇器似乎是一個(gè)速度問(wèn)題。但是,選擇器匹配性能很快。CSS 聲明對(duì)壓縮算法非常友好,因此優(yōu)化 CSS 選擇器所需的工作通常最好花在應(yīng)用程序的其他部分上,從而獲得更高的投資回報(bào)。
CSS 對(duì)于加載頁(yè)面和令人愉悅的用戶(hù)體驗(yàn)至關(guān)重要。雖然我們通??赡軙?huì)優(yōu)先考慮其他資產(chǎn)(例如腳本或圖像)的影響更大,但我們不應(yīng)該忘記 CSS。通過(guò)上述策略,您將能夠確??焖俳桓逗蛨?zhí)行。
原文:https://calibreapp.com/blog/css-performance