script標簽用於載入腳本與執行腳本,在前端開發中可以說是非常重要的標簽了。直接使用script腳本的話,html會按照順序來載入並執行腳本,在腳本載入&執行的過程中,會阻塞後續的DOM渲染。 現在大家習慣於在頁面中引用各種的第三方腳本,如果第三方服務商出現了一些小問題,比如延遲之類的,就會使得頁 ...
script
標簽用於載入腳本與執行腳本,在前端開發中可以說是非常重要的標簽了。
直接使用script
腳本的話,html
會按照順序來載入並執行腳本,在腳本載入&執行的過程中,會阻塞後續的DOM
渲染。
現在大家習慣於在頁面中引用各種的第三方腳本,如果第三方服務商出現了一些小問題,比如延遲之類的,就會使得頁面白屏。
好在script
提供了兩種方式來解決上述問題,async
和defer
,這兩個屬性使得script
都不會阻塞DOM
的渲染。
但既然會存在兩個屬性,那麼就說明,這兩個屬性之間肯定是有差異的。
defer
如果
script
標簽設置了該屬性,則瀏覽器會非同步的下載該文件並且不會影響到後續DOM
的渲染;
如果有多個設置了defer
的script
標簽存在,則會按照順序執行所有的script
;defer
腳本會在文檔渲染完畢後,DOMContentLoaded
事件調用前執行。
我們做了一個測試頁面,頁面中包含了兩個script
標簽的載入,給他們都加上defer
標識。
P.S. 為了更直觀,我們給script1.js
添加了1s
的延遲,給script2.js
添加了2s
的延遲。
下圖是頁面載入的過程&script
腳本的輸出順序。
不難看出,雖然script1
載入用時雖然比script2
短,但因為defer
的限制,所以Ta只能等前邊的腳本執行完畢後才能執行。
async
async
的設置,會使得script
腳本非同步的載入併在允許的情況下執行async
的執行,並不會按著script
在頁面中的順序來執行,而是誰先載入完誰執行。
我們修改測試頁面如下:
遂得到瞭如下的結果,頁面載入時長上,並沒有什麼變化,畢竟都是非同步載入的腳本。
但是我們可以看到一個小細節,DOMContentLoaded
事件的觸發並不受async
腳本載入的影響,在腳本載入完之前,就已經觸發了DOMContentLoaded
。
我們接著修改測試頁面。載入一個沒有延遲的script
腳本,使得腳本可以即時的載入完畢。
我們要測試一下,如果async
腳本載入的足夠快,是否會在DOMContentLoaded
之前就執行(這個實驗是基於對async
的描述“在允許的情況下執行”的論證)。
同時為了保證測試的穩定性,我們在script
腳本引入的後邊添加了數千個空的div
節點,用來延長文檔的渲染時間。
執行結果不出所料,如果給async
一定的時間,是有可能在DOMContentLoaded
事件之前就執行的。
P.S. 從上圖中左上角的火焰圖中,我們也能看到,出現了多段的藍色(更新:晚上寫的時候懵了,紫色的才是渲染,藍色的是解析)文檔渲染。以及下邊Console
的順序。
說明的確,async
的執行是載入完成就會去執行,而不像defer
那樣要等待所有的腳本載入完後按照順序執行。
畫幾張圖簡要說明
網上有了不少這種類似的圖,但是基本都是拿一個script就舉例的
未免太過寒酸,so咱們來一個豪華版,來畫一下多個腳本載入時的甘特圖
就像近年來各大手機廠商,出新機都喜歡來一個X+X plus
拿四個不同的顏色來標明各自代表的含義
普通script
文檔解析的過程中,如果遇到script
腳本,就會停止頁面的渲染進行下載(但是並不會影響後續的解析,解析和渲染是兩碼事兒)。
資源的下載是在解析過程中進行的,雖說script1
腳本會很快的載入完畢,但是他前邊的script2
並沒有載入&執行,所以他只能處於一個掛起的狀態,等待script2
執行完畢後再執行。
當這兩個腳本都執行完畢後,才會繼續渲染頁面。
defer
文檔解析時,遇到設置了defer
的腳本,就會在後臺進行下載,但是並不會阻止文檔的渲染,當頁面解析&渲染完畢後。
會等到所有的defer
腳本載入完畢並按照順序執行,執行完畢後會觸發DOMContentLoaded
事件。
async
async
腳本會在載入完畢後執行。async
腳本的載入不計入DOMContentLoaded
事件統計,也就是說下圖兩種情況都是有可能發生的
推薦的應用場景
defer
如果你的腳本代碼依賴於頁面中的DOM
元素(文檔是否渲染完畢),或者被其他腳本文件依賴。
例:
- 評論框
- 代碼語法高亮
polyfill.js
async
如果你的腳本並不關心頁面中的DOM
元素(文檔是否渲染完畢),並且也不會產生其他腳本需要的數據。
例:
- 百度統計
如果不太能確定的話,用defer
總是會比async
穩定。。。
參考資料
測試代碼存放處:https://github.com/Jiasm/research-script-tagclone
後執行npm start
即可運行。
調試推薦使用chrome
無痕模式(這樣才不會在Performance
頁簽上看到不相關的插件數據)。