JS和CSS執行順序
一、指令碼和樣式表載入、執行順序總結
1、指令碼
web的模式是同步的,開發者希望解析到一個script標籤時立即解析執行指令碼,並阻塞文件的解析直到指令碼執行完。如果指令碼是外引的,則網路必須先請求到這個資源——這個過程也是同步的,會阻塞文件的解析直到資源被請求到。這個模式保持了很多年,並且在html4及html5中都特別指定了。開發者可以將指令碼標識為defer,以使其不阻塞文件解析,並在文件解析結束後執行。Html5增加了標記指令碼為非同步的選項,以使指令碼的解析執行使用另一個執行緒。
2、樣式表(Style sheets)
樣式表採用另一種不同的模式。理論上,既然樣式表不改變Dom樹,也就沒有必要停下文件的解析等待它們,然而,存在一個問題,指令碼可能在文件的解析過程中請求樣式資訊,如果樣式還沒有載入和解析,指令碼將得到錯誤的值,顯然這將會導致很多問題,這看起來是個邊緣情況,但確實很常見。Firefox在存在樣式表還在載入和解析時阻塞所有的指令碼,而Chrome只在當指令碼試圖訪問某些可能被未載入的樣式表所影響的特定的樣式屬性時才阻塞這些指令碼。
3、預解析(Speculative parsing)和prefetch 優化
Webkit和Firefox都做了這個優化,當執行指令碼時,另一個執行緒解析剩下的文件,並載入後面需要通過網路載入的資源。這種方式可以使資源並行載入從而使整體速度更快。需要注意的是,預解析並不改變Dom樹,它將這個工作留給主解析過程,自己只解析外部資源的引用,比如外部指令碼、樣式表及圖片。
因此,可能你會看到在後面的指令碼先下載,在前面的指令碼才執行。
(個人對預解析和prefetch優化理解:預先解析<script>、<link>等標籤,預先載入這些資源。但請求到的內容之後不會去具體執行)。
來看個預解析的例項:
上面程式碼在的網路請求如下圖,有一個點:mod_36ad799.js等幾個js為什麼會在hm.js之前下載?
按照“指令碼會阻塞文件執行,直到指令碼完成下載並執行“理論來看,應該是等hm.js下載完成後才下載後面的js。
但現代瀏覽器有預解析機制,因此會預先載入js資源,但js執行會按照順序來執行。因此其他mod_36ad799.js會先下載,hm.js是要等到執行js時才去載入。
4、載入順序原因分析:
JS 和 CSS 在頁面中的位置,會影響其他資源(指 img 等非 js 和 css 資源)的載入順序,究其原因,有三個值得注意的點:
JS 有可能會修改 DOM。典型的,可能會有 document.write.。這意味著,在當前 JS 載入和執行完成前,後續所有資源的下載有可能是沒必要的。這是 JS 阻塞後續資源下載的根本原因。
JS 的執行有可能依賴最新樣式。比如,可能會有 var width = $('#id').width(). 這意味著,JS 程式碼在執行前,瀏覽器必須保證在此 JS 之前的所有 css(無論外鏈還是內嵌)都已下載和解析完成。這是 CSS 阻塞後續 JS 執行的根本原因。
現代瀏覽器很聰明,會進行預解析(Speculative parsing)和prefetch 優化 。這意味著,在不存在任何阻塞的情況下,理論上 JS 和 CSS 的下載時機都非常優先,和位置無關。但是不會執行<script>的內容,包括繫結的事件。
四、指令碼和樣式表放置位置建議
首頁的樣式表放在<head>標籤中,可以避免IE出現FOUC(無樣式閃爍,剛開始沒有樣式,後來獲取到樣式檔案,突然有樣式)。
指令碼儘量放在<body>標籤最後面,可以減少白屏時間,加快首屏出現速度。
五、<script>和<link>的onload觸發順序
onload不是在檔案下載完後觸發,而是在執行完js檔案或者解析完css檔案時觸發。
<script>一定比它前面的<script>晚觸發onload。<link>不一定比它前面的<link>晚觸發onload,後出現的<script>也不一定會比前面的<link>晚觸發,這跟檔案大小等因素是有關係的。
jquery-1.9.0.js和bootstrap.min.css比較大。而test.js和test.css很小。
<link rel="stylesheet" href="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css" onload='console.log("1")'/>
<link rel="stylesheet" href="test.css" onload='console.log("2")'/>
<script src="https://code.jquery.com/jquery-1.9.0.js" onload='console.log("3")'></script>
<script src="test.js" onload='console.log("4")'></script>
test.js: