Chrome 瀏覽器有意改變<link rel="stylesheet">
的加載方式,當(dāng)其出現(xiàn)在<body>
中時黍檩,這一變化將更加明顯娄蔼。筆者決定在本文中進(jìn)行詳細(xì)說明這種改變可能帶來影響與好處色徘。
一.目前CSS文件的加載方式
…content…
CSS 會阻礙渲染,因此在all-of-my-styles.css
全部加載完之前项贺,用戶就只能面對一片空白的屏幕。
通常,我們將某個站點(diǎn)的所有 CSS 樣式合并為一到兩個資源开缎,這意味著用戶會下載一堆當(dāng)前頁面根本就用不上的規(guī)則棕叫。這是因?yàn)榫W(wǎng)站可能包含許多不同類型的頁面,每個頁面都有自己的「組件」奕删;而在組件級別傳遞 CSS 的話谍珊,會降低 HTTP/1 的性能。
然而急侥,對 SPDY 和 HTTP/2 來說砌滞,事實(shí)卻并非如此。在這些協(xié)議中坏怪,許多小資源只需要很小的代價就能完成遞送贝润,并且被獨(dú)立緩存。
…content…
這樣一來就解決了冗余問題铝宵,但也意味著你需要知道輸出<head>
時頁面將包含的內(nèi)容打掘,從而防止 streaming。與此同時鹏秋,瀏覽器還是只能等待所有 CSS 樣式加載完畢尊蚁,才能開始渲染。如果加載 /site-footer.css
的速度不夠快侣夷,就會耽誤所有頁面的渲染横朋。
二.目前最先進(jìn)的 CSS 加載方法
// https://github.com/filamentgroup/loadCSS
!function(e){"use strict"
var n=function(n,t,o){function i(e){return f.body?e():void setTimeout(function(){i(e)})}var d,r,a,l,f=e.document,s=f.createElement("link"),u=o||"all"
return t?d=t:(r=(f.body||f.getElementsByTagName("head")[0]).childNodes,d=r[r.length-1]),a=f.styleSheets,s.rel="stylesheet",s.href=n,s.media="only x",i(function(){d.parentNode.insertBefore(s,t?d:d.nextSibling)}),l=function(e){for(var n=s.href,t=a.length;t--;)if(a[t].href===n)return e()
setTimeout(function(){l(e)})},s.addEventListener&&s.addEventListener("load",function(){this.media=u}),s.onloadcssdefined=l,l(function(){s.media!==u&&(s.media=u)}),s}
"undefined"!=typeof exports?exports.loadCSS=n:e.loadCSS=n}("undefined"!=typeof global?global:this)
/* The styles for the site header, plus: */
.main-article,
.comments,
.about-me,
footer {
display: none;
}
loadCSS("/the-rest-of-the-styles.css");
在上面的代碼中,通過一些內(nèi)聯(lián)樣式我們可以加速初始渲染百拓,同時隱藏起還沒有加載完樣式的組件琴锭,并通過 JavaScript 異步地完成加載。剩余的 CSS 加載完后會重寫.main-article
中的display:none
衙传。
這個方法受到性能專家的推崇,他們認(rèn)為這是快速完成初始渲染的好方法决帖,并且經(jīng)過實(shí)地測量確實(shí)在加載的時候快了不少。
但也存在一些不足之處蓖捶。地回。。俊鱼。刻像。。
「1.它需要一個(小的)JavaScript 庫」
這是由 WebKit 的實(shí)現(xiàn)方式造成的亭引。一旦頁面中添加了<link rel="stylesheet">
绎速,即使樣式表是由 JavaScript 加載的,WebKit 還是會在加載完成之前阻礙渲染焙蚓。
在 Firefox 和 IE/Edge 瀏覽器中纹冤,通過 JS 加載樣式表是完全異步進(jìn)行的洒宝。穩(wěn)定版本的 Chrome 瀏覽器是通過 WebKit 的方式加載的,但在 Canary 版本中萌京,仍然是使用 Firefox/Edge 加載方式雁歌。
「2.必須經(jīng)歷兩個加載階段」
在上述模式中,內(nèi)聯(lián)的 CSS 通過display:none
隱藏了沒有加載完樣式的內(nèi)容知残,直到異步加載完剩余的 CSS 樣式靠瞎。如果你將這些樣式分派到兩個或多個 CSS 文件中,這些文件有可能不按照順序加載求妹,導(dǎo)致加載過程中出現(xiàn)內(nèi)容錯亂:
內(nèi)容錯亂乏盐,就好比彈出廣告一樣,會導(dǎo)致用戶體驗(yàn)挫敗制恍,必須全力消滅父能。
既然有兩個加載階段,你就必須決定渲染的先后順序净神。你當(dāng)然會想首先渲染「位置顯要」的內(nèi)容何吝。但是,所謂的「位置」是根據(jù)窗口大小來決定的鹃唯。因此爱榕,問題來了,你得找出一把「萬能」鑰匙坡慌。
三.一個更簡單黔酥、更好的方法
…
…
…
…
…
計(jì)劃是這樣的:針對每個<link rel="stylesheet">
,加載樣式表時我們阻止渲染它的后續(xù)內(nèi)容八匠,但是允許渲染它之前的內(nèi)容絮爷。樣式表是并行加載的,但是按照一定的順序顯示梨树。這使得<link rel="stylesheet">
的效用與<script src="…"></script>
相近。
假設(shè)網(wǎng)站 header岖寞、正文和 footer 的 CSS 已經(jīng)加載完畢抡四,但其余內(nèi)容仍在等待,那么頁面會是這樣的:
- Header:已渲染
- 正文:已渲染
- 評論部分:未渲染仗谆,它前面的 CSS 還未被加載(
/comment.css
)指巡。 - 關(guān)于本站:未渲染。它前面的 CSS 還未被加載(
/comment.css
)隶垮。 - Footer:未渲染藻雪。盡管它本身的 CSS 已加載完成,但它前面的 CSS 還未被加載(
/comment.css
)狸吞。
這是一個按順序渲染的頁面勉耀。你不需要決定哪部分內(nèi)容在「顯要位置」指煎,只要在頁面組件第一次實(shí)例化之前引入該組件的 CSS 即可。它完全兼容 Streaming便斥,因?yàn)槌悄阈枰寥溃駝t不必要輸出<link>
。
當(dāng)使用內(nèi)容決定布局的布局系統(tǒng)時(例如表格和 flexbox)枢纠,要注意避免加載時出現(xiàn)內(nèi)容錯位像街。這不是什么新問題了,但是分步渲染會使得它出現(xiàn)得更為頻繁晋渺。你可以通過 hack flexbox 來解決镰绎,但對整體頁面布局來說,使用 CSS grid 工具效果更佳(不過對小一些的組件來說木西,flexbox 還是很棒的)跟狱。
四.Chrome瀏覽器的改變
HTML 規(guī)范并沒有規(guī)定 CSS 應(yīng)當(dāng)怎樣阻止頁面渲染,它不鼓勵在 body 中使用<link rel="stylesheet">
户魏,但是所有的瀏覽器都允許使用驶臊。當(dāng)然了,瀏覽器們在處理 body 中的 link 時都有自己的方法:
Chrome和Safari:一旦發(fā)現(xiàn)
<link rel="stylesheet">
就停止渲染叼丑,并且在已發(fā)現(xiàn)的樣式表全部完成加載之前不會開始渲染关翎。這會導(dǎo)致<link>
前未被渲染的內(nèi)容也被阻塞。Firefox: head中的
<link rel="stylesheet">
會阻塞渲染鸠信,直至所有已發(fā)現(xiàn)的樣式表加載完畢纵寝,body中的<link rel="stylesheet">
并不阻塞任何渲染,除非某個 head 中的樣式表已經(jīng)阻塞了渲染星立,這會導(dǎo)致無樣式的內(nèi)容出現(xiàn)閃爍(FOUC)爽茴。IE/Edge: 阻塞解析器直到樣式表加載完畢,但是允許渲染
<link>
之前的內(nèi)容绰垂。
在 Chrome 團(tuán)隊(duì)室奏,我們喜歡 IE/Edge 的方式,所以打算跟它看齊劲装。這就允許上文描述的漸進(jìn)式 CSS 渲染方式胧沫。我們正在努力把它變成標(biāo)準(zhǔn),從允許<body>
中的<link>
開始占业。
目前 Chrome/Safari 采用的方式是向下兼容的绒怨,帶來的問題是阻塞渲染的時間比實(shí)際需要的長。Firefox 的方式稍微復(fù)雜一些谦疾,但有個解決的方法:
「Firefixing南蹂!」
因?yàn)?Firefox 并不總是為了<body>
中的<link>
阻塞渲染,我們得為這個多花點(diǎn)功夫來避免 FOUC念恍。謝天謝地這很容易六剥,因?yàn)?code><script>會阻塞解析晚顷,同時也會等掛起的樣式表完成加載:
…
此處的<script>
元素必須是非空的,但加個空格足矣仗考。
Firefox 和 Edge/IE 可以實(shí)現(xiàn)很美好的漸進(jìn)式渲染音同,而 Chrome 和 Safari 在所有 CSS 加載完畢之前只能給你看一張白屏。目前 Chrome/Safari 采用的方式怎么都比將所有的樣式表都放<head>
里要強(qiáng)秃嗜,所以你現(xiàn)在就可以開始采用這個方法了权均。后面幾個月,Chrome 會遷移到 Edge 的模式锅锨,這樣用戶就能體驗(yàn)更快的渲染速度了叽赊。
就是這樣!通過更簡單的方法加載你需要的 CSS必搞,強(qiáng)力提升渲染速度必指。
五.快速定位 CSS 加載問題
那么問題來了,怎么樣才能知道是不是 css 加載影響了頁面的性能呢恕洲?只有定位到問題確實(shí)是 css 塔橡,老板才會給你時間和人力來優(yōu)化這方面的問題對不對?
[圖片上傳失敗...(image-9abe03-1566958590683)]
筆者之前做過前端優(yōu)化的工作霜第,國內(nèi)外的前端性能優(yōu)化工具也使用了不少葛家,現(xiàn)階段可以較好實(shí)現(xiàn)這個定位頁面慢加載因素的工具有: OneAPM Browser Insight、AppDynamics泌类、Ruxit癞谒,大家有興趣的話可以去嘗試下。
注:本文原文作者為 Jake Archibald刃榨,由 OneAPM 運(yùn)營人員翻譯整理