隨著網際網路發展至今,對於網站來說,效能顯的越來越重要了,CSS作為頁面渲染和內容展現的重要環節,影響著使用者對整個網站的第一體驗。所以,我們需要重視與CSS相關的效能優化。【推薦學習:】
專案開發初期我們可能因為各種原因(很大一部分原因是因為專案工期,產品往往把專案上線時間卡的死死的,根本不聽你說的什麼效能優化),怎麼寫的舒服就怎麼來,對於效能優化我們常常在專案完成時才去考慮,經常被推遲到專案的末期,甚至到暴露出嚴重的效能問題時才進行效能優化。
為了更多地避免這一情況,首先要重視起效能優化相關的工作,將其貫穿到整個產品設計與開發中。其次,就是了解效能相關的內容,在專案開發過程中,自然而然地進行效能優化。
想要優化CSS的效能,我們首先需要了解CSS的渲染規則,CSS選擇器是從右向左進行匹配的
來看個例子:
.nav h3 a{font-size: 14px;}複製程式碼
渲染過程大概是:首先找到所有的a
,沿著a
的父元素查詢h3
,然後再沿著h3
,查詢.nav
。中途找到了符合匹配規則的節點就加入結果集。如果找到根元素html
都沒有匹配,則不再遍歷這條路徑,從下一個a
開始重複這個查詢匹配(只要頁面上有多個最右節點為a
)。
Tips:為什麼CSS選擇器是從右向左匹配的?
CSS中更多的選擇器是不會匹配的,所以在考慮效能問題時,需要考慮的是如何在選擇器不匹配時提升效率。從右向左匹配就是為了達成這一目的的,通過這一策略能夠使得CSS選擇器在不匹配的時候效率更高。這樣想來,在匹配時多耗費一些效能也能夠想的通了。
效能優化中有一個重要的指標——首次有效繪製(First Meaningful Paint,簡稱FMP)即指頁面的首要內容(primary content)出現在螢幕上的時間。這一指標影響使用者看到頁面前所需等待的時間,而 內聯首屏關鍵CSS(即Critical CSS,可以稱之為首屏關鍵CSS) 能減少這一時間。
很多人都喜歡通過link
標籤參照外部CSS檔案
。但需要知道的是,將CSS直接內聯到HTML檔案中能使CSS更快速地下載。而使用外部CSS檔案時,需要在HTML檔案下載完成後才知道所要參照的CSS檔案,然後才下載它們。所以說,內聯CSS能夠使瀏覽器開始頁面渲染的時間提前,因為在HTML下載完成之後就能渲染了。
但是我們不應該將所有的CSS都內聯在HTML檔案中,因為[初始擁塞視窗]存在限制(TCP相關概念,通常是 14.6kB,壓縮後大小)
,如果內聯CSS後的檔案超出了這一限制,系統就需要在伺服器和瀏覽器之間進行更多次的往返,這樣並不能提前頁面渲染時間。因此,我們應當只將渲染首屏內容所需的關鍵CSS內聯到HTML中。
⚠️還有一點需要注意的是內聯CSS沒有快取,每次都會隨HTML的載入而重新下載,但我們將內聯首屏關鍵CSS控制在 14.6kB
以內,它對效能優化還是起到正向作用的。(凡事有利也有弊)
我們需要知道兩點內容:(具體可以看我之前的文章:)
CSS
不會阻塞DOM
的解析,但會阻塞DOM
的渲染CSS
會阻塞JS
執行,但不會阻塞JS
檔案的下載由於CSS會阻塞DOM的渲染,所以我們將首屏關鍵CSS內聯後,剩餘的非首屏CSS內容可以使用外部CSS,並且非同步載入,防止非首屏CSS內容阻塞頁面的渲染。
CSS非同步載入方式
第一種方法是動態建立
// 建立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元素的media
屬性設定為使用者瀏覽器不匹配的媒體型別(或媒體查詢)
對瀏覽器來說,如果樣式表不適用於當前媒體型別,其優先順序會被放低,會在不阻塞頁面渲染的情況下再進行下載。在首屏檔案載入完成之後,將media
的值設為screen
或all
,從而讓瀏覽器開始解析CSS。
<link rel="stylesheet" href="mystyles.css" media="noexist" onload="this.media='all'">
第三種方法是通過rel
屬性將link
元素標記為alternate
可選樣式表
<link rel="alternate stylesheet" href="mystyles.css" onload="this.rel='stylesheet'">
第四種方法是使用rel=preload
來非同步載入CSS
<link rel="preload" href="mystyles.css" as="style" onload="this.rel='stylesheet'">
注意,as
是必須的。忽略as
屬性,或者錯誤的as
屬性會使preload
等同於XHR
請求,瀏覽器不知道載入的是什麼內容,因此此類資源載入優先順序會非常低。as
的可選值可以參考上述標準檔案。
看起來,rel="preload"
的用法和上面兩種沒什麼區別,都是通過更改某些屬性,使得瀏覽器非同步載入CSS檔案但不解析,直到載入完成並將修改還原,然後開始解析。
但是它們之間其實有一個很重要的不同點,那就是使用preload,比使用不匹配的media
方法能夠更早地開始載入CSS。所以儘管這一標準的支援度還不完善,仍建議優先使用該方法。
這應該是最容易想到的一個方法了,通過壓縮CSS檔案大小來提高頁面載入速度。現在的構建工具,如webpack、gulp/grunt、rollup等也都支援CSS壓縮功能。壓縮後的檔案能夠明顯減小,可以大大降低了瀏覽器的載入時間。
一般情況下,元素的巢狀層級不能超過3級,過度的巢狀會導致程式碼變得臃腫,沉餘,複雜。導致css檔案體積變大,造成效能浪費,影響渲染的速度!而且過於依賴HTML檔案結構。這樣的css樣式,維護起來,極度麻煩,如果以後要修改樣式,可能要使用!important
覆蓋。儘量保持簡單,不要使用巢狀過多過於複雜的選擇器。
一般情況下,會存在這兩種無用的CSS程式碼:一種是不同元素或者其他情況下的重複程式碼,一種是整個頁面內沒有生效的CSS程式碼。
對於前者,在編寫的程式碼時候,我們應該儘可能地提取公共類,減少重複。對於後者,在不同開發者進行程式碼維護的過程中,總會產生不再使用的CSS的程式碼,當然一個人編寫時也有可能出現這一問題。而這些無用的CSS程式碼不僅會增加瀏覽器的下載量,還會增加瀏覽器的解析時間,這對效能來說是很大的消耗。所以我們需要找到並去除這些無用程式碼。
那麼我們如何知道哪些CSS程式碼是無用程式碼呢?
谷歌的Chrome瀏覽器就有這種開箱即用的功能。只需轉到檢視>開發人員>開發人員工具,並在最近的版本中開啟Sources索引標籤,然後開啟命令選單。然後,點選Coverage,在Coverage analysis視窗中高亮顯示當前頁面上未使用的程式碼。
我們有時候可能會寫下面這種程式碼來消除一些標籤的預設樣式或統一瀏覽器對標籤渲染的差異化:
*{ margin:0; padding:0; }
這樣雖然程式碼量少,但它的效能可不是最佳的,我們最好還是寫對應的標籤選擇器:
body,dl,dd,h1,h2,h3,h4,h5,h6,p,form,ol,ul{ margin:0; padding:0; }
開發時儘量避免使用萬用字元選擇器
一般來講一個網站上肯定會有很多個小圖示,對於這些小圖示,目前的主流的解決方案有三個,cssSprite(雪碧圖),字型圖示,把圖片轉成base64。
cssSprite
,只需要請求一次,大大的減少了http請求。缺點就是管理不靈活,如果需要新增一個圖示,都需要改合併圖片的原始檔,圖示定位也要規範,不然容易干擾圖片之間的定位。8K以下的圖片才轉換成base64編碼。
不建議使用@import
主要有以下兩點原因:
使用@import
引入CSS會影響瀏覽器的並行下載。使用@import
參照的CSS檔案只有在參照它的那個css檔案被下載、解析之後,瀏覽器才會知道還有另外一個css需要下載,這時才去下載,然後下載後開始解析、構建render tree等一系列操作。這就導致瀏覽器無法並行下載所需的樣式檔案。
多個@impor
t會導致下載順序紊亂。在IE中,@import
會引發資原始檔的下載順序被打亂,即排列在@import後面的js檔案先於@import下載,並且打亂甚至破壞@import自身的並行下載。
在ID選擇器前面巢狀其它選擇器純粹是多餘的
.content #text
)完全是浪費效能。div#text
或者.box#text
。這兩種方式完全是多餘的,理由就是ID在頁面就是唯一的。前面加任何東西都是多餘的!CSS 支援多種單位和數位格式,可以刪除尾隨和跟隨的零,零始終是零,新增維度不會為包含的資訊附帶任何價值。
.box { padding: .2px; margin: 20px; avalue: 0; }
在網站的使用過程中,某些操作會導致樣式的改變,這時瀏覽器需要檢測這些改變並重新渲染,其中有些操作所耗費的效能更多。我們都知道,當FPS為60時,使用者使用網站時才會感到流暢。這也就是說,我們需要在16.67ms內完成每次渲染相關的所有操作,所以我們要儘量減少耗費更多的操作。
減少迴流與重繪
合併對DOM
樣式的修改,採用css class
來修改
const el = document.querySelector('.box') el.style.margin = '5px' el.style.borderRadius = '12px' el.style.boxShadow = '1px 3px 4px #ccc'
建議使用css class
.update{ margin: 5px; border-dadius: 12px; box-shadow: 1px 3px 4px #ccc } const el = document.querySelector('.box') el.classList.add('update')
如果需要對DOM進行多次存取,儘量使用區域性變數快取該DOM
避免使用table佈局,可能很⼩的⼀個⼩改動會造成整個table的重新佈局
CSS選擇符從右往左匹配查詢,避免節點層級過多
DOM離線處理,減少迴流重繪次數
離線的DOM不屬於當前DOM樹中的任何一部分,這也就意味著我們對離線DOM處理就不會引起頁面的迴流與重繪。
display: none
,上面我們說到了 (display: none
) 將元素從渲染樹中完全移除,元素既不可見,也不是佈局的組成部分,之後在該DOM上的操作不會觸發迴流與重繪,操作完之後再將display
屬性改為顯示,只會觸發這一次迴流與重繪。提醒⏰:visibility : hidden
的元素只對重繪有影響,不影響重排。
dom
檔案片段,在它上面批次操作 dom
,操作完成之後,再新增到檔案中,這樣只會觸發一次重排。const el = document.querySelector('.box') const fruits = ['front', 'nanjiu', 'study', 'code']; const fragment = document.createDocumentFragment(); fruits.forEach(item => { const li = document.createElement('li'); li.innerHTML = item; fragment.appendChild(li); }); el.appendChild(fragment);
const el = document.querySelector('.box') const fruits = ['front', 'nanjiu', 'study', 'code']; const cloneEl = el.cloneNode(true) fruits.forEach(item => { const li = document.createElement('li'); li.innerHTML = item; cloneEl.appendChild(li); }); el.parentElement.replaceChild(cloneEl,el)
DOM脫離普通檔案流
使用absoult
或fixed
讓元素脫離普通檔案流,使用絕對定位會使的該元素單獨成為渲染樹中 body
的一個子元素,重排開銷比較小,不會對其它節點造成太多影響。
CSS3硬體加速(GPU加速)
使用css3硬體加速,可以讓transform、opacity、filters
這些動畫不會引起迴流重繪 。但是對於動畫的其它屬性,比如background-color
這些,還是會引起迴流重繪的,不過它還是可以提升這些動畫的效能。
常見的觸發硬體加速的css屬性:
將節點設定為圖層
圖層能夠阻⽌該節點的渲染⾏為影響別的節點。⽐如對於video標籤來說,瀏覽器會⾃動將該節點變為圖層。
具體迴流與重繪知識點可以看我這篇文章:https://juejin.cn/post/7064077572132323365
(學習視訊分享:)
以上就是CSS如何進行效能優化?優化小技巧分享的詳細內容,更多請關注TW511.COM其它相關文章!