CSS 加載

原文地址:
CSS 加載新方式
The future of loading CSS

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 InsightAppDynamics泌类、Ruxit癞谒,大家有興趣的話可以去嘗試下。

注:本文原文作者為 Jake Archibald刃榨,由 OneAPM 運(yùn)營人員翻譯整理

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末弹砚,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子枢希,更是在濱河造成了極大的恐慌桌吃,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晴玖,死亡現(xiàn)場離奇詭異读存,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)呕屎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來敬察,“玉大人秀睛,你說我怎么就攤上這事×觯” “怎么了蹂安?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵椭迎,是天一觀的道長。 經(jīng)常有香客問我田盈,道長畜号,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任允瞧,我火速辦了婚禮简软,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘述暂。我一直安慰自己痹升,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布畦韭。 她就那樣靜靜地躺著疼蛾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪艺配。 梳的紋絲不亂的頭發(fā)上察郁,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機(jī)與錄音转唉,去河邊找鬼皮钠。 笑死,一個胖子當(dāng)著我的面吹牛酝掩,可吹牛的內(nèi)容都是我干的鳞芙。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼期虾,長吁一口氣:“原來是場噩夢啊……” “哼原朝!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起镶苞,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤喳坠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后茂蚓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壕鹉,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年聋涨,在試婚紗的時候發(fā)現(xiàn)自己被綠了晾浴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡牍白,死狀恐怖脊凰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情茂腥,我是刑警寧澤狸涌,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布切省,位于F島的核電站,受9級特大地震影響帕胆,放射性物質(zhì)發(fā)生泄漏朝捆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一懒豹、第九天 我趴在偏房一處隱蔽的房頂上張望芙盘。 院中可真熱鬧,春花似錦歼捐、人聲如沸何陆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贷盲。三九已至,卻和暖如春剥扣,著一層夾襖步出監(jiān)牢的瞬間巩剖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工钠怯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留佳魔,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓晦炊,卻偏偏與公主長得像鞠鲜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子断国,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容