Chrome 瀏覽器有意改變<link rel="stylesheet">
的加載方式,當其出如今<body>
中時,這一變化將更加明顯。筆者決定在本文中進行詳細說明這種改變可能帶來影響與好處。css
#####一.目前CSS文件的加載方式html
<head> <link rel="stylesheet" href="/all-of-my-styles.css"> </head> <body> …content… </body>
CSS 會阻礙渲染,所以在all-of-my-styles.css
所有加載完以前,用戶就只能面對一片空白的屏幕。前端
一般,咱們將某個站點的全部 CSS 樣式合併爲一到兩個資源,這意味着用戶會下載一堆當前頁面根本就用不上的規則。這是由於網站可能包含許多不一樣類型的頁面,每一個頁面都有本身的「組件」;而在組件級別傳遞 CSS 的話,會下降 HTTP/1 的性能。git
然而,對 SPDY 和 HTTP/2 來講,事實卻並不是如此。在這些協議中,許多小資源只須要很小的代價就能完成遞送,而且被獨立緩存。github
<head> <link rel="stylesheet" href="/site-header.css"> <link rel="stylesheet" href="/article.css"> <link rel="stylesheet" href="/comment.css"> <link rel="stylesheet" href="/about-me.css"> <link rel="stylesheet" href="/site-footer.css"> </head> <body> …content… </body>
這樣一來就解決了冗餘問題,但也意味着你須要知道輸出<head>
時頁面將包含的內容,從而防止 streaming。與此同時,瀏覽器仍是隻能等待全部 CSS 樣式加載完畢,才能開始渲染。若是加載 /site-footer.css
的速度不夠快,就會耽誤全部頁面的渲染。瀏覽器
#####二.目前最早進的 CSS 加載方法緩存
<head> <script> // 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) </script> <style> /* The styles for the site header, plus: */ .main-article, .comments, .about-me, footer { display: none; } </style> <script> loadCSS("/the-rest-of-the-styles.css"); </script> </head> <body> </body>
在上面的代碼中,經過一些內聯樣式咱們能夠加速初始渲染,同時隱藏起尚未加載完樣式的組件,並經過 JavaScript 異步地完成加載。剩餘的 CSS 加載完後會重寫.main-article
中的display:none
。性能優化
這個方法受到性能專家的推崇,他們認爲這是快速完成初始渲染的好方法,而且通過實地測量確實在加載的時候快了很多。微信
但也存在一些不足之處。。。。。。app
「1.它須要一個(小的)JavaScript 庫」
這是由 WebKit 的實現方式形成的。一旦頁面中添加了<link rel="stylesheet">
,即便樣式表是由 JavaScript 加載的,WebKit 仍是會在加載完成以前阻礙渲染。
在 Firefox 和 IE/Edge 瀏覽器中,經過 JS 加載樣式表是徹底異步進行的。穩定版本的 Chrome 瀏覽器是經過 WebKit 的方式加載的,但在 Canary 版本中,仍然是使用 Firefox/Edge 加載方式。
「2.必須經歷兩個加載階段」
在上述模式中,內聯的 CSS 經過display:none
隱藏了沒有加載完樣式的內容,直到異步加載完剩餘的 CSS 樣式。若是你將這些樣式分派到兩個或多個 CSS 文件中,這些文件有可能不按照順序加載,致使加載過程當中出現內容錯亂:
內容錯亂,就比如彈出廣告同樣,會致使用戶體驗挫敗,必須全力消滅。
既然有兩個加載階段,你就必須決定渲染的前後順序。你固然會想首先渲染「位置顯要」的內容。可是,所謂的「位置」是根據窗口大小來決定的。所以,問題來了,你得找出一把「萬能」鑰匙。
#####三.一個更簡單、更好的方法
<head> </head> <body> <!-- HTTP/2 push this resource, or inline it, whichever's faster --> <link rel="stylesheet" href="/site-header.css"> <header>…</header> <link rel="stylesheet" href="/article.css"> <main>…</main> <link rel="stylesheet" href="/comment.css"> <section class="comments">…</section> <link rel="stylesheet" href="/about-me.css"> <section class="about-me">…</section> <link rel="stylesheet" href="/site-footer.css"> <footer>…</footer> </body>
計劃是這樣的:針對每一個<link rel="stylesheet">
,加載樣式表時咱們阻止渲染它的後續內容,可是容許渲染它以前的內容。樣式表是並行加載的,可是按照必定的順序顯示。這使得<link rel="stylesheet">
的效用與<script src="…"></script>
相近。
假設網站 header、正文和 footer 的 CSS 已經加載完畢,但其他內容仍在等待,那麼頁面會是這樣的:
/comment.css
)。/comment.css
)。/comment.css
)。這是一個按順序渲染的頁面。你不須要決定哪部份內容在「顯要位置」,只要在頁面組件第一次實例化以前引入該組件的 CSS 便可。它徹底兼容 Streaming,由於除非你須要,不然沒必要要輸出<link>
。
當使用內容決定佈局的佈局系統時(例如表格和 flexbox),要注意避免加載時出現內容錯位。這不是什麼新問題了,可是分步渲染會使得它出現得更爲頻繁。你能夠經過 hack flexbox 來解決,但對總體頁面佈局來講,使用 CSS grid 工具效果更佳(不過對小一些的組件來講,flexbox 仍是很棒的)。
#####四.Chrome瀏覽器的改變
HTML 規範並無規定 CSS 應當怎樣阻止頁面渲染,它不鼓勵在 body 中使用<link rel="stylesheet">
,可是全部的瀏覽器都容許使用。固然了,瀏覽器們在處理 body 中的 link 時都有本身的方法:
**Chrome和Safari:**一旦發現 <link rel="stylesheet">
就中止渲染,而且在已發現的樣式表所有完成加載以前不會開始渲染。這會致使<link>
前未被渲染的內容也被阻塞。
Firefox: head中的<link rel="stylesheet">
會阻塞渲染,直至全部已發現的樣式表加載完畢,body中的<link rel="stylesheet">
並不阻塞任何渲染,除非某個 head 中的樣式表已經阻塞了渲染,這會致使無樣式的內容出現閃爍(FOUC)。
IE/Edge: 阻塞解析器直到樣式表加載完畢,可是容許渲染<link>
以前的內容。
在 Chrome 團隊,咱們喜歡 IE/Edge 的方式,因此打算跟它看齊。這就容許上文描述的漸進式 CSS 渲染方式。咱們正在努力把它變成標準,從容許<body>
中的<link>
開始。
目前 Chrome/Safari 採用的方式是向下兼容的,帶來的問題是阻塞渲染的時間比實際須要的長。Firefox 的方式稍微複雜一些,但有個解決的方法:
「Firefixing!」
由於 Firefox 並不老是爲了<body>
中的<link>
阻塞渲染,咱們得爲這個多花點功夫來避免 FOUC。謝天謝地這很容易,由於<script>
會阻塞解析,同時也會等掛起的樣式表完成加載:
<link rel="stylesheet" href="/article.css"><script> </script> <main>…</main>
此處的<script>
元素必須是非空的,但加個空格足矣。
Firefox 和 Edge/IE 能夠實現很美好的漸進式渲染,而 Chrome 和 Safari 在全部 CSS 加載完畢以前只能給你看一張白屏。目前 Chrome/Safari 採用的方式怎麼都比將全部的樣式表都放<head>
裏要強,因此你如今就能夠開始採用這個方法了。後面幾個月,Chrome 會遷移到 Edge 的模式,這樣用戶就能體驗更快的渲染速度了。
就是這樣!經過更簡單的方法加載你須要的 CSS,強力提高渲染速度。
#####五.快速定位 CSS 加載問題
那麼問題來了,怎麼樣才能知道是否是 css 加載影響了頁面的性能呢?只有定位到問題確實是 css ,老闆纔會給你時間和人力來優化這方面的問題對不對?
筆者以前作過前端優化的工做,國內外的前端性能優化工具也使用了很多,現階段能夠較好實現這個定位頁面慢加載因素的工具備: OneAPM Browser Insight、AppDynamics、Ruxit,你們有興趣的話能夠去嘗試下。
注:本文原文做者爲 Jake Archibald,由 OneAPM 運營人員翻譯整理
原文地址:https://jakearchibald.com/2016/link-in-body/
Browser Insight 是一個基於真實用戶的 Web 前端性能監控平臺,可以幫你們定位網站性能瓶頸,網站加速效果可視化;支持瀏覽器、微信、App 瀏覽 HTML 和 HTML5 頁面。想閱讀更多技術文章,請訪問 OneAPM 官方技術博客。
本文轉自 OneAPM 官方博客