我來填坑了,CSS篇終于寫出來了,如果你沒看過前面的JS篇,可以在這裡觀看。
衆所周知,CSS的加載會阻塞浏覽器渲染或是引起浏覽器重繪,目前業界普遍推薦把CSS放到 <head>
中,防止在CSS還沒加載完,DOM就已經繪制出來了,造成CSS加載完成後的重繪。那在現代浏覽器中我們有沒有辦法提高首屏渲染速度那?
你是不是經常在第一次打開某個網站的時候看到這種情況,本來的頁面是這樣的
實際上剛加載出來的是這樣的
字型檔案沒加載出來,或者加載的太慢了
了解CSS解析過程
以下面這段HTML為例,解釋一遍CSS加載解析的過程。
<html>
<head>
<!-- headStyle.css中存在字型檔案webfont.woff2 -->
<link rel="stylesheet" type="text/css" href="/headStyle.css" target="_blank" rel="external nofollow" >
</head>
<body>
<p>Text</p>
<link rel="stylesheet" type="text/css" href="/bodyEndStyle.css" target="_blank" rel="external nofollow" >
</body>
</html>
浏覽器自上而下讀取HTML文檔,當發現headStyle.css的時候,停止Parser HTML,開始下載下傳headStyle.css,解析headStyle.css的過程中發現字型檔案webfont.woff2,開始下載下傳webfont.woff2,并繼續解析css生成CSSStyleSheet。解析完畢後,繼續Parser HTML,當發現p标簽時,會将p标簽結合目前的CSSStyleSheet展示出來,此時使用者螢幕中已經有p标簽的内容了。當浏覽器發現bodyEndStyle.css時,就會下載下傳headStyle.css,解析CSS,然後更新CSSStyleSheet,這時會引起一次重繪。當字型下載下傳完畢的時候也會引起一次重繪。
這個過程中,有兩個非常嚴重的問題。一、如果headStyle.css檔案很大,浏覽器需要解析很多行CSS後才能還有個字型檔案需要下載下傳,其實此時已經很晚了,字型下載下傳時間稍長一點,就會出現我前面截圖提到的問題。二、bodyEndStyle.css中如果存在p标簽對應的樣式,那p标簽的樣式會在bodyEndStyle.css解析完成後,改變一次樣式,很影響體驗。
如何解決這些問題那?其中也會用到一些JS篇中提到的點,如果沒看過,建議先看看。
優化核心依舊是減少下載下傳時間
JS篇中的預先解析DNS(dns-prefetch)依舊适用,提前解析CSS檔案所在域名的DNS。
Preload
因為CSS已經在head中,我們不需要為css加preload屬性了,但是css中用到的字型檔案,一定要在所有css之前proload上。
<link rel="preload" href="/webfont.woff2" target="_blank" rel="external nofollow" as="font">
首頁CSS内聯,非必要CSS異步加載
首頁用到的CSS内聯寫在
<head>
中,其餘CSS均采用異步加載,可以采用這種自己實作的加載CSS的方法,在合适的需要時加載需要的css
function LoadStyle(url) {
try {
document.createStyleSheet(url)
} catch(e) {
var cssLink = document.createElement('link');
cssLink.rel = 'stylesheet';
cssLink.type = 'text/css';
cssLink.href = url;
var head = document.getElementsByTagName('head')[0];
head.appendChild(cssLink)
}
}
如果你使用webpack,那就更輕松了,使用import函數,大緻如下
// 在a.js子產品中直接引入css
import 'style.css'
// 在需要a.js子產品的地方
improt('path-of-a.js').then(module => {})
webpack打包後,其實是把style.css打包進了a.js,在異步加載a.js的時候,會将style.css中的代碼插入
haed
标簽中。
終極完美結構
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Faster</title>
<link rel="dns-prefetch" href="//cdn.cn/" target="_blank" rel="external nofollow" >
<link rel="preload" href="//cdn.cn/webfont.woff2" target="_blank" rel="external nofollow" as="font">
<link rel="preload" href="//cdn.cn/Page1-A.js" target="_blank" rel="external nofollow" as="script">
<link rel="preload" href="//cdn.cn/Page1-B.js" target="_blank" rel="external nofollow" as="script">
<link rel="prefetch" href="//cdn.cn/Page2.js" target="_blank" rel="external nofollow" >
<link rel="prefetch" href="//cdn.cn/Page3.js" target="_blank" rel="external nofollow" >
<link rel="prefetch" href="//cdn.cn/Page4.js" target="_blank" rel="external nofollow" >
<style type="text/css">
/* 首頁用到的CSS内聯 */
</style>
</head>
<body>
<script type="text/javascript" src="//cdn.cn/Page1-A.js" defer></script>
<script type="text/javascript" src="//cdn.cn/Page1-B.js" defer></script>
</body>
</html>
在JS篇)中,我已經解釋過這套結構中JS的執行順序了,本篇隻是加入了CSS和字型。至此,我心中終極完美的頁面HTML結構就是這樣了。
如果你對異步加載CSS的方案感興趣,歡迎留言與我讨論!
擴充閱讀
- 浏覽器的工作原理
- 關于Preload, 你應該知道些什麼?
- Preload,Prefetch 和它們在 Chrome 之中的優先級