前端css性能優化
1. 減少樣式表數量和壓縮文件大小:
通過合并多個樣式表、刪除未使用的樣式、壓縮樣式表等方式來減少樣式表數量和大小,從而減少網絡請求和提高加載速度。
-
通常來說,樣式文件會被瀏覽器緩存,進入到其他頁面樣式文件不用再去下載。一般是樣式表數量越少越好,但是如果樣式文件偏大,也會影響加載速度,所以需要取舍。如果是大項目,應該合并成不同的樣式文件,如果是簡單的項目,建議合并成一個文件即可。如果無法確認項目規模,建議分開成不同的樣式文件,日后要合并也比較方便。
-
瀏覽器想要渲染出網頁必須要先將CSS等文件下載下來,所有文件越小,那么就能夠更快的下載和渲染,特別是在一些網絡速度比較慢的場景下效果尤為明顯。我們可以借助一些打包工具進行壓縮,比如webpack、gulp/grunt、rollup等。
2. 不要嵌套過多復雜的選擇器:
使用盡可能簡潔的選擇器來匹配元素,避免使用過于復雜的選擇器,可以減少匹配時間和提高渲染速度。
如下:
法2的寫法,性能更高,因為css選擇器是從右到左解析的,法2可以直接獲取child,而法1需要先獲取所有的child,再獲取family下的所有child
/* 法1 */
.family .child {font-size: 14xp;
}
/* 法2 */
.child {font-size: 14xp;
}
3. 避免使用通配符*選擇器:
通配符選擇器(*)會匹配所有元素,會消耗大量計算資源。盡可能避免使用通配符選擇器,或者將其限制在具體的元素或類別上。
4. 使用ID選擇器代替類選擇器:
ID選擇器比類選擇器更具體,匹配速度更快。在需要使用樣式的地方,盡可能使用ID選擇器代替類選擇器。
5. 避免在全局范圍內使用過多的類選擇器:
為了提高選擇器的效率,可以限定類選擇器的范圍,避免在全局范圍內使用過多的類選擇器。
6. 減少全局選擇器的使用:
全局選擇器會匹配文檔中的每一個元素,因此會降低選擇器的效率。應盡量避免使用全局選擇器,而是限定選擇器的范圍。
7. 使用子選擇器:
子選擇器的效率比后代選擇器高,因為子選擇器只會選擇直接子元素,而后代選擇器會選擇所有后代元素。因此,應盡量使用子選擇器來提高選擇器的效率。
8. 使用類選擇器代替標簽選擇器:
類選擇器比標簽選擇器的效率高。因此,應盡量使用類選擇器來代替標簽選擇器,從而提高選擇器的效率。
9. 避免使用@import:
@import會阻塞頁面的加載,影響瀏覽器的并行下載,并且會增加頁面的請求次數。使用@import引用的CSS文件只有在引用它的那個css文件被下載、解析之后,瀏覽器才會知道還有另外一個css需要下載,這時才去下載,然后下載后開始解析、構建render tree等一系列操作,這就導致瀏覽器無法并行下載所需的樣式文件。盡量避免使用@import,或者將其放在樣式表的開頭。
10. 避免使用過多的浮動和定位:
浮動、定位元素不僅會影響其他元素的布局和渲染,也容易造成重繪與回流。如果使用過多的浮動與定位,會導致頁面的渲染速度變慢。盡量避免使用過多的浮動與定位,或者使用其他布局方式代替。
11. 使用雪碧圖:
將多個小圖標合并成一個大圖,并使用CSS來控制顯示區域,可以減少網絡請求和提高加載速度。
12. 避免使用!important:
!important會覆蓋其他所有樣式,會增加計算時間和降低渲染速度。盡量避免使用!important,或者將其限制在必要的地方。
13. 使用縮寫屬性:
使用縮寫屬性可以減少樣式表的大小,從而提高加載速度。例如,使用margin代替margin-top、margin-right、margin-bottom、margin-left。
14. 慎用一些需要瀏覽器計算的CSS屬性:
如果某些CSS屬性需要瀏覽器計算,那都是需要耗費一些性能的,比如box-shadow、border-radius、filter、透明度、:nth-child、calc等等,而且除了計算,這些屬性也容易造成重繪和重排
如果非必要情況下可以不必使用這些屬性,當然,大多數都是必要情況。
15. 利用繼承減少代碼量:
我們都知道CSS有一些屬性是可以繼承的,比如color,font-size,font-family等等,但是很多開發人員不注意這種細節,編寫很多重復性代碼,從而使得CSS文件變得比較大,影響加載速度。
16. 首屏關鍵css使用內聯樣式:
網站性能是留住用戶的關鍵,那么網站首屏的加載速度更是關鍵的關鍵。
通常我們不會去寫內聯樣式,因為這會造成代碼維護困難,而且內聯樣式瀏覽器不會緩存的,每次刷新都會重新加載css。
但是,凡事都有兩面性,內聯CSS能夠使頁面渲染的開始時間提前,因為在HTML下載完成之后就能渲染了,不像link那樣引用樣式需要耗費更多時間。
那么,我們是否可以全部使用內聯樣式呢?答案是否定的,剛剛說了內聯樣式還是存在許多問題的,所以最好的解決辦法就是:首屏關鍵CSS可以使用內聯的形式。
17. 異步加載CSS:
CSS會阻塞渲染,在CSS文件請求、下載、解析完成之前,瀏覽器將不會渲染任何已處理的內容。
但是,有些CSS文件可能不是我們渲染必須的CSS文件,我們可以讓它異步加載,從而提升渲染速度,比如下面兩種異步加載CSS的方式:
-
使用JavaScript動態創建樣式表link元素,并插入到DOM中
// 創建link標簽 const myCSS = document.createElement( "link" ); myCSS.rel = "stylesheet"; myCSS.href = "mystyles.css"; // 插入到header的最后位置 document.head.insertBefore( myCSS, document.head.childNodes[ document.head.childNodes.length - 1 ].nextSibling );
-
修改link標簽(有兼容性問題)
<link rel="preload" href="mystyles.css" as="style" onload="this.rel='stylesheet'">
或者
<link rel="alternate stylesheet" href="mystyles.css" onload="this.rel='stylesheet'">
18. 不要為了速度而放棄了可維護性和可讀性:
渲染速度很重要,但是我們在編碼過程中,也需要注意代碼的可維護性和可讀性,比如,不能盲目的為了壓縮樣式文件的大小,寫一些很簡單的類名,如a,b,c,d等
19. 減少頁面重排、重繪:
頁面的重排重繪都會耗費瀏覽器性能,我們在非必要情況下應該避免,比如下面這些情況。
改變元素的內外邊距
CSS偽類激活
能使用background-color,就盡量不要使用background
改變font-size和font-family
當然有些情況我們是不能避免重繪和回流的,我們視情況而定
21.將css文件放在頁面最上面:
我們可以看到,大多數的html頁面在用link引入外部css文件的時候,都是把css文件放在頁面的頂部,即放在標簽里面,那么,這樣做的原因是什么呢?
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><!-- css放頂部 --><link rel="stylesheet" href="path/to/your/css/file.css">
</head>
<body><!-- 頁面內容 -->
</body>
</html>
答案:可以讓前端性能更優化
那么,為什么就可以讓前端性能更加優化呢?這時,我們就需要先了解腳本和樣式文件對頁面渲染的影響。
瀏覽器從服務器獲取文檔并自上而下進行解析,在腳本文件不包含defer和async的情況下,會按照如下的規則進行解析:
- 解析html文檔,遇到html標簽時,構建DOM樹
- 在解析html文檔的時候,如果遇到外聯的css文件或js文件,會阻塞html文檔繼續向下解析。
- css文件下載完畢之后,會構建 CSS Rule Tree,js文件下載完畢之后,會立即解析執行。
- 如果CSS Rule Tree先于DOM Tree生成,那么DOM Tree會在一邊構建的同時一邊結合CSS Rule Tree生成Rendering Tree(渲染樹),渲染頁面。
- 如果CSS Rule Tree后于DOM Tree生成,那么在構建完DOM Tree之后,會再結合CSS Rule Tree樹,再次構建一遍,渲染頁面,這就會導致重繪與回流,影響性能
從上面的規則中我們可以看到,我們css文件放在頂部,就可以先于DOM樹生成CSS Rule Tree,DOM Tree就只會構建一遍,節省了時間,前端性能就得到優化。
當然,這個用法并不是絕對的,如果css文件比較大,下載時間較長,屏幕就會出現較長的白屏時間,用戶體驗不好。
20.其它優化小點:
去除空規則:{}
屬性值為0時,不加單位
盡量少的去對標簽進行選擇,而是用class
盡量少的去使用后代選擇器,降低選擇器的權重值。后代選擇器的開銷是最高的,盡量將選擇器的深度降到最低,最高不要超過三層,更多的使用類來關聯每一個標簽元素
屬性值為浮動小數0.**,可以省略小數點之前的0
標準化各種瀏覽器前綴:帶瀏覽器前綴的在前。標準屬性在后
選擇器優化嵌套,盡量避免層級過深