更新時(shí)間:2018年09月13日16時(shí)19分 來(lái)源:傳智播客 瀏覽次數(shù):
下面我們開始介紹實(shí)踐型的4個(gè)優(yōu)化技巧,先從首屏關(guān)鍵CSS開始。
1. 內(nèi)聯(lián)首屏關(guān)鍵CSS(Critical CSS)
性能優(yōu)化中有一個(gè)重要的指標(biāo)——首次有效繪制(First Meaningful Paint,簡(jiǎn)稱FMP)即指頁(yè)面的首要內(nèi)容(primary content)出現(xiàn)在屏幕上的時(shí)間。這一指標(biāo)影響用戶看到頁(yè)面前所需等待的時(shí)間,而**內(nèi)聯(lián)首屏關(guān)鍵CSS(即Critical CSS,可以稱之為首屏關(guān)鍵CSS)**能減少這一時(shí)間。
大家應(yīng)該都習(xí)慣于通過(guò)link標(biāo)簽引用外部CSS文件。但需要知道的是,將CSS直接內(nèi)聯(lián)到HTML文檔中能使CSS更快速地下載。而使用外部CSS文件時(shí),需要在HTML文檔下載完成后才知道所要引用的CSS文件,然后才下載它們。所以說(shuō),內(nèi)聯(lián)CSS能夠使瀏覽器開始頁(yè)面渲染的時(shí)間提前,因?yàn)樵贖TML下載完成之后就能渲染了。
既然內(nèi)聯(lián)CSS能夠使頁(yè)面渲染的開始時(shí)間提前,那么是否可以內(nèi)聯(lián)所有的CSS呢?答案顯然是否定的,這種方式并不適用于內(nèi)聯(lián)較大的CSS文件。因?yàn)槌跏紦砣翱?存在限制(TCP相關(guān)概念,通常是 14.6kB,壓縮后大小),如果內(nèi)聯(lián)CSS后的文件超出了這一限制,系統(tǒng)就需要在服務(wù)器和瀏覽器之間進(jìn)行更多次的往返,這樣并不能提前頁(yè)面渲染時(shí)間。因此,我們應(yīng)當(dāng)只將渲染首屏內(nèi)容所需的關(guān)鍵CSS內(nèi)聯(lián)到HTML中。
既然已經(jīng)知道內(nèi)聯(lián)首屏關(guān)鍵CSS能夠優(yōu)化性能了,那下一步就是如何確定首屏關(guān)鍵CSS了。顯然,我們不需要手動(dòng)確定哪些內(nèi)容是首屏關(guān)鍵CSS。Github上有一個(gè)項(xiàng)目Critical CSS4,可以將屬于首屏的關(guān)鍵樣式提取出來(lái),大家可以看一下該項(xiàng)目,結(jié)合自己的構(gòu)建工具進(jìn)行使用。當(dāng)然為了保證正確,大家最好再親自確認(rèn)下提取出的內(nèi)容是否有缺失。
不過(guò)內(nèi)聯(lián)CSS有一個(gè)缺點(diǎn),內(nèi)聯(lián)之后的CSS不會(huì)進(jìn)行緩存,每次都會(huì)重新下載。不過(guò)如上所說(shuō),如果我們將內(nèi)聯(lián)后的文件大小控制在了14.6kb以內(nèi),這似乎并不是什么大問(wèn)題。
如上,我們已經(jīng)介紹了為什么要內(nèi)聯(lián)關(guān)鍵CSS以及如何內(nèi)聯(lián),那么剩下的CSS我們?cè)趺刺幚砗媚?建議使用外部CSS引入剩余CSS,這樣能夠啟用緩存,除此之外還可以異步加載它們。
2. 異步加載CSSCSS會(huì)阻塞渲染,在CSS文件請(qǐng)求、下載、解析完成之前,瀏覽器將不會(huì)渲染任何已處理的內(nèi)容。有時(shí),這種阻塞是必須的,因?yàn)槲覀儾⒉幌M谒璧腃SS加載之前,瀏覽器就開始渲染頁(yè)面。那么將首屏關(guān)鍵CSS內(nèi)聯(lián)后,剩余的CSS內(nèi)容的阻塞渲染就不是必需的了,可以使用外部CSS,并且異步加載。
那么如何實(shí)現(xiàn)CSS的異步加載呢?有以下四種方式可以實(shí)現(xiàn)瀏覽器異步加載CSS。
第一種方式是使用JavaScript動(dòng)態(tài)創(chuàng)建樣式表link元素,并插入到DOM中。
// 創(chuàng)建link標(biāo)簽const myCSS = document.createElement( "link" );myCSS.rel = "stylesheet";myCSS.href = "mystyles.css";// 插入到header的最后位置document.head.insertBefore( myCSS, document.head.childNodes[ document.head.childNodes.length - 1 ].nextSibling );
第二種方式是將link元素的media屬性設(shè)置為用戶瀏覽器不匹配的媒體類型(或媒體查詢),如media="print",甚至可以是完全不存在的類型media="noexist"。對(duì)瀏覽器來(lái)說(shuō),如果樣式表不適用于當(dāng)前媒體類型,其優(yōu)先級(jí)會(huì)被放低,會(huì)在不阻塞頁(yè)面渲染的情況下再進(jìn)行下載。
當(dāng)然,這么做只是為了實(shí)現(xiàn)CSS的異步加載,別忘了在文件加載完成之后,將media的值設(shè)為screen或all,從而讓瀏覽器開始解析CSS。
復(fù)制代碼與第二種方式相似,我們還可以通過(guò)rel屬性將link元素標(biāo)記為alternate可選樣式表,也能實(shí)現(xiàn)瀏覽器異步加載。同樣別忘了加載完成之后,將rel改回去。
復(fù)制代碼上述的三種方法都較為古老?,F(xiàn)在,rel="preload"5這一Web標(biāo)準(zhǔn)指出了如何異步加載資源,包括CSS類資源。
復(fù)制代碼注意,as是必須的。忽略as屬性,或者錯(cuò)誤的as屬性會(huì)使preload等同于XHR請(qǐng)求,瀏覽器不知道加載的是什么內(nèi)容,因此此類資源加載優(yōu)先級(jí)會(huì)非常低。as的可選值可以參考上述標(biāo)準(zhǔn)文檔。
看起來(lái),rel="preload"的用法和上面兩種沒什么區(qū)別,都是通過(guò)更改某些屬性,使得瀏覽器異步加載CSS文件但不解析,直到加載完成并將修改還原,然后開始解析。
但是它們之間其實(shí)有一個(gè)很重要的不同點(diǎn),那就是使用preload,比使用不匹配的media方法能夠更早地開始加載CSS。所以盡管這一標(biāo)準(zhǔn)的支持度還不完善,仍建議優(yōu)先使用該方法。
該標(biāo)準(zhǔn)現(xiàn)在已經(jīng)是候選標(biāo)準(zhǔn),相信瀏覽器會(huì)逐漸支持該標(biāo)準(zhǔn)。在各瀏覽器的支持度如下圖所示。
從上圖可以看出這一方法在現(xiàn)在的瀏覽器中支持度不算樂(lè)觀,不過(guò)我們可以通過(guò)loadCSS進(jìn)行polyfill,所以支持不支持,這都不是事兒。
3. 文件壓縮性能優(yōu)化時(shí)有一個(gè)最容易想到,也最常使用的方法,那就是文件壓縮,這一方案往往效果顯著。
文件的大小會(huì)直接影響瀏覽器的加載速度,這一點(diǎn)在網(wǎng)絡(luò)較差時(shí)表現(xiàn)地尤為明顯。相信大家都早已習(xí)慣對(duì)CSS進(jìn)行壓縮,現(xiàn)在的構(gòu)建工具,如webpack、gulp/grunt、rollup等也都支持CSS壓縮功能。壓縮后的文件能夠明顯減小,可以大大降低了瀏覽器的加載時(shí)間。
4. 去除無(wú)用CSS雖然文件壓縮能夠降低文件大小。但CSS文件壓縮通常只會(huì)去除無(wú)用的空格,這樣就限制了CSS文件的壓縮比例。那是否還有其他手段來(lái)精簡(jiǎn)CSS呢?答案顯然是肯定的,如果壓縮后的文件仍然超出了預(yù)期的大小,我們可以試著找到并刪除代碼中無(wú)用的CSS。
一般情況下,會(huì)存在這兩種無(wú)用的CSS代碼:一種是不同元素或者其他情況下的重復(fù)代碼,一種是整個(gè)頁(yè)面內(nèi)沒有生效的CSS代碼。對(duì)于前者,在編寫的代碼時(shí)候,我們應(yīng)該盡可能地提取公共類,減少重復(fù)。對(duì)于后者,在不同開發(fā)者進(jìn)行代碼維護(hù)的過(guò)程中,總會(huì)產(chǎn)生不再使用的CSS的代碼,當(dāng)然一個(gè)人編寫時(shí)也有可能出現(xiàn)這一問(wèn)題。而這些無(wú)用的CSS代碼不僅會(huì)增加瀏覽器的下載量,還會(huì)增加瀏覽器的解析時(shí)間,這對(duì)性能來(lái)說(shuō)是很大的消耗。所以我們需要找到并去除這些無(wú)用代碼。
當(dāng)然,如果手動(dòng)刪除這些無(wú)用CSS是很低效的。我們可以借助Uncss庫(kù)來(lái)進(jìn)行。Uncss可以用來(lái)移除樣式表中的無(wú)用CSS,并且支持多文件和JavaScript注入的CSS。
作者:傳智播客前端與移動(dòng)開發(fā)培訓(xùn)學(xué)院
首發(fā):http://web.itcast.cn
北京校區(qū)