不簡單的前端效能優化

2021-09-16 23:01:40 字數 3863 閱讀 2075

本文主要介紹「關鍵渲染路徑」與「網路」兩個方面的效能優化並提供demo,篇幅較長建議電腦**。

前端優化的方面太多,本文介紹的僅僅是其中的一部分,力求涵蓋「關鍵渲染路徑」的方方面面,及一些不常被提到的「網路優化」部分。

測試環境如無特殊說明均為chrome 57

瀏覽器從開啟乙個url到渲染完頁面共有:

執行js指令碼

通過html文件構建dom(parse html)

通過css檔案構建cssom(parse css)

通過dom與cssom計算render tree

根據render tree進行繪製,計算各個元素位置與大小(layout)

對頁面進行上色,渲染為最終顯示的畫素(paint)

第一次完成paint稱為「初次渲染」,這時候使用者就能看到render tree裡面的東西了。而完成初次渲染的過程稱為「關鍵渲染路徑」,關鍵渲染路徑上需要載入的資源叫做「關鍵資源」

這個過程很多很複雜,其中的依賴關係也很複雜,筆者嘗試畫圖來表示,但是實在是沒畫出來,所以還是用文本來表述吧:

比較有意思的是,字型的載入會阻塞區域性的渲染。若某一段文字的字型使用了乙個尚未載入完的字型,這段文字則先不會被paint,直到字型載入完或者超過某個時間(通常是3秒)文字才會突然顯示。

瀏覽器為了避免fout(flash of unstyled text),會盡量等待字型載入完成後,再顯示應用了該字型的內容。只有當字型超過一段時間仍未載入成功時,瀏覽器才會降級使用系統字型。每個瀏覽器都規定了自己的超時時間(chrome是3秒)。但這也帶來了foit(flash of invisible text)問題。內容無法盡快地被展示,導致空白

css會阻塞layout:demo

css會阻塞js執行:demo

js執行會阻塞關鍵渲染路徑,哪怕是defer還是async:demo

foot會阻塞區域性渲染,但是智慧型的瀏覽器會給他設定乙個上限,一般是3秒鐘:demo

優化核心概念是:將初次渲染不需要的css想辦法剝離出關鍵渲染路徑

如果僅僅是為了提前初次渲染時間而進行優化,將頁面必備的css剝離關鍵渲染路徑而造成樣式突變導致頁面抖動,則得不償失了

對某些**查詢條件觸發後才使用的css,可以在link標籤中加入media屬性,如下:

此樣式表仍會載入。當瀏覽器環境不匹配**查詢條件時,該樣式表不會阻塞渲染。我們可針對不同**環境拆分css檔案,並為link標籤新增**查詢,避免為了載入非關鍵css資源,而阻塞初次渲染

可以使用js**來新增css

var style = document.createelement('link');

style.rel = 'stylesheet';

style.href = 'index.css';

將link標籤的rel屬性設定為preload,瀏覽器遇到遇到標記為preload的link時,會開始載入它,但是由於rel不是stylesheet,因此不會阻塞渲染。

然後在適當的時候,在rel改為stylesheet,即可應用此樣式。

但是這個屬性相容性比較差,詳細可以參考這裡。不過有乙個polyfill可以用loadcss,原理是通過dom api插入樣式資源。

這個屬性的使用情景有些偏,也可能是我理解問題:

當使用preload引入css檔案時,實際上證明這個頁面根本不需要這個css,它有可能是列印樣式,或者是響應式**的另一套css**。但是,使用preload屬性,瀏覽器反而會預先載入它,也就是說,在window.onload之前,使用者將耗費了網路資源在載入乙個暫時不需要的樣式。網路資源不可能是無限的,也就是說這個css會占用頁面其他資源比如的網路資源。

詢問瓜瓜老師本人後,瓜瓜老師說:

舉個例子。第三屏有個廣告版,它的樣式

這樣確實這個css的緊急程度就介於關鍵渲染路徑的css與頁面之間了,不過貌似這個情景很受限。

當script標籤擁有defer屬性時,該指令碼會被推遲到整個html文件解析完後,再開始執行。因此將指令碼放在head中,可以提早瀏覽器對指令碼檔案的載入,但是卻不會阻塞parse html。

注意,defer的指令碼不會被css阻塞,parse html完成後立即執行,但是有可能會阻塞關鍵渲染路徑。為什麼說有可能呢,假如指令碼檔案在render tree生成前載入完畢,則會開始執行,執行過程中會阻塞關鍵渲染路徑。請參考這個demo

被defer的指令碼,在執行時會嚴格按照在html文件**現的順序執行,但是實際上貌似不是這樣,js檔案前後檔案若有依賴需慎重使用。

和defer類似,只是當js載入完後馬上執行,而不在乎parse html是否完成,因此假如指令碼比css先載入完,也會阻塞關鍵渲染路徑。

據筆者所知,這是唯一一種100%不會阻塞關鍵渲染路徑的js指令碼載入方式。通過dom api引入的js指令碼會等到頁面layout和paint後再開始執行,不論你將載入js檔案的**放在head中還是body後面亦是如此。

若不想讓字型阻塞區域性渲染,可使用web font loader

網路優化和css優化策略相同,盡可能讓關鍵資源提前載入完,所以優化時盡量將以下指標壓縮到最低:

當然,如果你的專案使用了先進的spdy或http/2,下面的方法可能並不適用。

每乙個請求,若使用網域名稱,則需要額外增加一次dns查詢時間(若快取未過期會命中快取),因此乙個**過多的使用不同網域名稱的資源會額外增加dns查詢開銷,這點在移動端非常明顯。

當然,每個請求建立根據tcp協議規定,還需要先進行3次捂手才可以建立鏈結。

合併請求

現在的比較流行的webpack就非常擅長做這種事情

適度使用內聯css和js

使用內聯的css和js固然可以減少請求,但是使用內聯也意味著你的css和js將不會再被瀏覽器快取,因此要適度的使用內聯,內聯不是萬能的。

從http協議下手

最佳方案肯定是過渡到http/2無疑,但是現在http/2的支援並不算太好,而且各大瀏覽器僅支援tls下實現的http/2(說白了就是https),使得http/2的使用存在許些限制。

如果沒有http/2,或許可以:

適度使用網域名稱雜湊

不過,使用網域名稱雜湊要適度,每乙個網域名稱都需要額外的增加一次dns查詢時間。當然,dns本身也有快取,或許適當的增加dns ttl時間也是個不錯的主意。

對於js、css檔案,現在網上現成的壓縮工具一堆,而且應用十分廣泛,相信大家都知道了,這裡就不多說了。

說到壓縮,伺服器開啟一定的壓縮策略(如gzip)是個不錯的主意,效果拔群,資源大概會壓縮到原有的1/3左右。

壓縮,這個需要知道什麼情境下適合什麼型別的,gif、jpg、png使用情景各不相同,具體可以參考這篇文章:格式那麼多,哪種更適合你?

假如乙個頁面需要引入2個css才能工作,下面有2種方式

毫無疑問肯定是前者快,因為前者的網路來回數是1,而後者是2。

因此,盡可能將資源載入扁平化,減少關鍵資源網路來回數是個不錯的主意。

當然,優化時要注意的點也有不少,比如前面提到的瀏覽器同域併發限制等,需要權衡使其不要影響到其他的導致初次渲染時間延後。

使用document.write列印link標籤引入css仍會阻塞初次渲染。

奇舞團@瓜瓜老師:

奇舞團@屈屈老師:

w3c規範:

前端的效能優化

1.精簡 把重複的 提取出來,例如js,css呼叫那一大段 用外掛程式把js,css中的空格去掉。2.多利用快取機制,前端可以用last modified expires和etag。後台的話可以用redis。3.在html中不要放 後台傳檔案時用gzip壓縮 4.寫css檔案時,選擇器的層級不要超過...

前端效能優化

1.減少http請求次數 css sprites 在國內很多人叫css精靈,是一種網頁應用處理方式。它允許你將乙個頁面涉及到的所有零星都包含到一張大圖中去,這樣一來,當訪問該頁面時,載入的就不會像以前那樣一幅一幅地慢慢顯示出來了。對於當前網路流行的速度而言,不高於200kb的單張的所需載入時間基本是...

前端效能優化

在開發好頁面後,如何讓頁面更好更快的執行 從前端的角度來看,效能可以分為兩個方向 從使用者角度來看,乙個是頁面載入的很快,另乙個是頁面使用起來非常流暢 輸入了url 瀏覽器開始解析 查詢本地快取 dns解析 建立連線 伺服器處理 伺服器響應 客戶端收到響應 解析html 然後開始渲染頁面 使用者可以...