1. 程式人生 > >解決阻塞效應之defer和async

解決阻塞效應之defer和async

1、正常的網頁載入流程

1. 瀏覽器一邊下載HTML網頁,一邊開始解析
2. 解析過程中,發現<script>標籤
3. 暫停解析,網頁渲染的控制權轉交給JavaScript引擎
4. 如果<script>標籤引用了外部指令碼,就下載該指令碼,否則就直接執行
5. 執行完畢,控制權交還渲染引擎,恢復往下解析HTML網頁

如果外部指令碼載入時間很長(比如一直無法完成下載),就會造成網頁長時間失去響應,瀏覽器就會呈現“假死”狀態,這被稱為“阻塞效應”。

為了避免這種情況,較好的做法是將<script>標籤都放在頁面底部,而不是頭部。這樣做有兩個好處,

1、遇到指令碼失去響應,網頁主體的渲染也已經完成了,使用者至少可以看到內容,而不是面對一張空白的頁面。
2、在DOM結構生成之前就呼叫DOM,JavaScript會報錯,如果指令碼都在網頁尾部載入,就不存在這個問題,因為這時DOM肯定已經生成了。

2、defer屬性

<script src="a.js" defer></script>
<script src="b.js" defer></script>

defer的執行流程如下

1. 瀏覽器開始解析HTML網頁
2. 解析過程中,發現帶有defer屬性的script標籤
3. 瀏覽器繼續往下解析HTML網頁,同時並行下載script標籤中的外部指令碼


4. 瀏覽器完成解析HTML網頁,此時再執行下載的指令碼

只有等到DOM載入完成後,才會執行a.js和b.js

3、async屬性

<script src="a.js" async></script>
<script src="b.js" async></script>

async的執行流程如下

1. 瀏覽器開始解析HTML網頁
2. 解析過程中,發現帶有async屬性的script標籤
3. 瀏覽器繼續往下解析HTML網頁,同時並行下載script標籤中的外部指令碼
4. 指令碼下載完成,瀏覽器暫停解析HTML網頁,開始執行下載的指令碼


5. 指令碼執行完畢,瀏覽器恢復解析HTML網頁

async屬性可以保證指令碼下載的同時,瀏覽器繼續渲染。但一旦採用這個屬性,就無法保證指令碼的執行順序。哪個指令碼先下載結束,就先執行那個指令碼。

  總之,如果指令碼之間沒有依賴關係,就使用async屬性,如果指令碼之間有依賴關係,就使用defer屬性。如果同時使用async和defer屬性,後者不起作用,瀏覽器行為由async屬性決定。