這週閱讀到這篇有意思的文章,於是便動手寫下簡單的翻譯,若是有理解錯誤的地方歡迎指教。css
Chrome 正在試圖改變當 <link rel="stylesheet">
寫在 <body>
的行為,從blink-dev
的文章並不能很清楚的知道其優點。因此這篇文章想要深刻的介紹這點。html
blink 是 Chrome 和 Opera 渲染引擎,而 blink-dev 是其開發社羣git
<head> <link rel="stylesheet" href="/all-of-my-styles.css"> </head> <body> …content… </body>
當 CSS 段落在渲染時,會讓使用者瞪著白白的頁面直到 all-of-my-styles.css
徹底下載完畢。github
一般我們會把站內全部的 CSS 封裝成較少,可能只有一兩個資源檔,但這同時也意味著使用者須要下載大量的樣式設定(CSS Rules)卻沒有在該頁面使用。因為一個網站包含著各種不一樣的頁面與元件,而這些東西須要套用不一樣的樣式規則,若是所以分拆成許多檔案而產生大量的請求 request
這在 HTTP/1
是很是耗效能的。app
不過在 SPDY
和 HTTP/2
卻不是這樣,傳輸許多分散的小資源只會增長一點點的開銷,並且這些東西能夠個別暫存(cached)。flex
<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>
下 <link>
時,得要知道這些頁面各自須要哪些資源。
另外,瀏覽器在開始輸出以前,仍然得下載全部的 CSS。若是出現一個下載比較慢的 CSS 例如 /site-footer.css
將會形成渲染的東西延遲。請觀察範例。this
<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>
上面程式碼,我們有一些內嵌的樣式(inline style)來讓我們能夠快速的渲染初始化的樣式,接著把那些還沒取得樣式的部分隱藏起來,然後開始透過 Javascript 非同步
下載剩餘的樣式。這些剩餘的 CSS
會覆寫掉在 .main-article
和其餘選擇器內的 display: none
。google
這種第一次先快速的初始化渲染,然後持續匯入的方法是許多效能專家所推薦的。flexbox
原做者實做了wiki-offline並將狀況紀錄以下圖
上面這張圖片是在 3G 的環境下測試。不過這樣的方法還是有些不足的地方:
不幸的,因為 WebKit 的實做。當 <link rel="stylesheet">
一被加到頁面時,WebKit 會阻塞渲染(render),直到樣式都被載入,即便這個樣式是透過 Javascript 加入的。
在 Firefox 和 IE/Edge,透過 JS 加入的樣式徹底是非同步載入。Chrome 當前穩定版本然還是遵循 WebKit 的行為,不過在 Canary
已經跟 Firefox/Edge 一樣了。
根據上面的模式,內嵌 CSS(inline CSS) 透過 display: none
隱藏還沒有套用樣式的內容,然後非同步得載入 CSS 之後呈現內容。若是您須要增長多個 CSS 檔案那麼結果可能就是內容不按順序出現
若是內容還在異動的過程結果周圍的廣告就先出現這一般會讓使用者覺得不開心就關閉你的網站了。
所以被限制在只有兩個載入階段,你必須要決定哪些是第一次渲染時就要出現,哪些是比較晚的。當然,你但願上方的區塊越快顯示越好,不過所謂"上方的區塊"取決於 viewport
可視區域的大小。最後你可能決定定義一個尺寸範圍套用在全部人身上。
若是你想讓事情更加複雜,當然你能夠選擇客製 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">
在其下載樣式時去阻塞跟在後面的內容,但允許前面的內容先開始渲染。樣式表(CSS)自己的載入機制是平行的,可是套用樣式卻是要照順序的。這讓 <link rel="stylesheet">
的行為類似為 <script>
。
假設說 site-header
, article
, footer
的樣式已經載完了,但剩下的還沒,其行為以下:
Header: 已輸出
Article: 已輸出
Comments: 未呈現,在該標籤以前地 CSS 還沒載完(./comment.css
)
About me: 未呈現,在該標籤以前地 CSS 還沒載完(./comment.css
)
Footer: 未呈現,在該標籤以前地 CSS 還沒載完(./comment.css
),即便本身的 CSS 已經載完了
這讓我們能夠照順序輸出頁面。您甚至不須要決定哪些是"上面的區塊",只要在元件以前匯入元件須要的 CSS 便可。
不過你還是須要注意當使用內容決定佈局(layout system)
,例如 table, flexbox 時,在載入期間應避免內容異動。
這不是現在才產生的問題了,只是在逐步顯示
這種機制之下更常遇到。若是你看不懂這段在描述什麼看一下下面的影片就知道了。
意思是說雖然 flexbox
已經很不錯了,但 Grid 還是更推薦的 Layout system。
HTML規範並不涵蓋網頁渲染時是否應該或該如何被 CSS 阻塞,並且也不鼓勵把 <link rel="stylesheet">
寫在 body
中,不過全部瀏覽器都允許這麼作。
當然他們也都各自使用了本身的方式處理在 body 中的 <link>
Chrome & Safari: 一旦發現 <link rel="stylesheet">
就中止渲染(render),直到發現的樣式被載入完畢。每每導致在 <link>
上面還未渲染的內容被卡住。
Firefox: <link rel="stylesheet">
在 <head>
中會阻塞渲染直到全部發現的樣式都被載入完畢。在 body 中的話不會阻塞渲染,除非 head 裡已經有樣式阻塞住
這可能會導致 FOUC (Flash of unstyled content) ,就是畫面先以預設的樣子呈現後閃一下才套上樣式。
IE/Edge: 中斷分析器直到樣式載完,不過允許 <link>
上面的內容渲染(render)。
我們偏好像 IE/Edge 的行為,因此 Chrome 將會跟隨這樣的機制。目前 Chrome/Safari 的行為就是會卡比較久的時間,Firefox 的行為相對較複雜一些,不過有一些小技巧。
因為 Firefox 不會因為 body 中的 <link>
阻塞渲染。我們須要一點小技巧來避免 FOUC。幸虧有個很是簡單的方式就是透過 <script>
中斷解析器讓他等一等待處理狀態的樣式。
<link rel="stylesheet" href="/article.css"><script> </script> <main>…</main>
這個標籤不能徹底沒有內容,因此我們須要留一個"空白"。
Firefox 和 Edge/IE 將會循序的載入輸出,而 Chrome 和 Safari 則是先看到空白的頁面一陣子直到 CSS 所有載完。目前 Chrome/Safari 的行為比起把樣式連結放在 <head>
也沒有差到哪去,因此現在就能夠開始使用這個方式,很快的 Chrome 將會往 Edge 的機制修正,這麼一來渲染會更加迅速。
以上就是一個簡單的技巧協助我們加快速度並逐步載入 CSS