這裏詳細聊聊和script標籤相關的腳本執行順序。html
幾個首要特性:瀏覽器
defer
或async
屬性)的會阻止文檔渲染。相關腳本會當即下載並執行。document.currentScript
能夠得到當前正在運行的腳本(Chrome 29+, FF4+)假設以下簡單代碼1,最終會產生三個alert依次爲「A」、「B」、「C」。網絡
<!-- HTML Code --> <script>alert("A");</script> <script>alert("B");</script> <script>alert("C");</script>
咱們再考慮有網絡請求的狀況2:app
<!-- HTML code --> <script src="https://snipt.net/raw/7b08744009c450e07c0bfc1d606fc72e/"></script> <script src="https://snipt.net/raw/a2e8c05c1f6fc0e47d259aa899304e89/"></script> <script src="https://snipt.net/raw/4fab3017d3d46cbfc4bbd88aab006650/"></script>
三個文件都須要先下載再運行,且第二個文件的尺寸遠大於另外兩個文件。但結果依然是彈出三個alert,內容分別是」A」、」B」、」C」。異步
從上面兩個例子,能夠充分了解到script標籤的柱塞式執行。async
async屬性是HTML5的新特性3,這意味着其兼容性並不樂觀(IE10+)。fetch
async表示該script標籤並不柱塞,也不一樣步執行。瀏覽器只須要在腳本下載完畢後再執行便可——沒必要柱塞頁面渲染等待該腳本的下載和執行。.net
以下代碼4,會獲得三個alert,可是alert的內容分別是」A」,」C」,」B」。3d
<!-- HTML code --> <script src="https://snipt.net/raw/7b08744009c450e07c0bfc1d606fc72e/"></script> <script src="https://snipt.net/raw/a2e8c05c1f6fc0e47d259aa899304e89/" async=true></script> <script src="https://snipt.net/raw/4fab3017d3d46cbfc4bbd88aab006650/"></script>
能夠看到,第二個script標籤在加入async
並無阻止後續文檔解析和腳本執行。code
考究這個屬性產生的原有,其實有大量的腳本加載器在作這樣的事情:
var script = document.createElement("script"); script.src = "file.js"; document.body.appendChild(script);
不難想象,經過腳本異步插入的script標籤達到的效果和帶async屬性的script標籤是同樣的。換句話說,由腳本插入的script標籤默認是async的。
另外,對內聯腳本設置async屬性是沒有意義的,也不產生其餘效果。其包含的腳本老是當即執行的。
帶有defer屬性的腳本,一樣會推遲腳本的執行,而且不會阻止文檔解析。就如同這個腳本,放置到了文檔的末尾(</body>
以前)。
以下代碼5的宏觀現象和加了async屬性的例子是同樣的,都會獲得」A」、」C」、」B」的三個alert。可是其原理是不同的。
<!-- HTML code --> <script src="https://snipt.net/raw/7b08744009c450e07c0bfc1d606fc72e/"></script> <script src="https://snipt.net/raw/a2e8c05c1f6fc0e47d259aa899304e89/" defer=true></script> <script src="https://snipt.net/raw/4fab3017d3d46cbfc4bbd88aab006650/"></script>
defer屬性是會確保腳本在文檔解析完畢後執行的——即便這個腳本在文檔解析過程當中就已經下載完畢變成可執行的狀態,瀏覽器也會推遲這個腳本的執行,直到文檔解析完畢6,並在DOMContentLoaded以前7。
同時,帶有defer的腳本彼此之間,能保證其執行順序。
注意,defer屬性並非每一個瀏覽器支持,即使支持的瀏覽器,也會由於版本不同致使具體行爲不一致。另外,你們能夠經過將script標籤放置到文檔末尾這種簡單的作法達到defer屬性同樣的效果。
defer屬性早在IE4就被支持,可是這個defer屬性和現代瀏覽器的行爲是有區別的。只有IE10以上,纔開始按照標準執行defer屬性。
參考W3C的官方文檔8,defer和async兩個屬性是能夠互相影響的:
There are three possible modes that can be selected using these attributes. If the async attribute is present, then the script will be executed asynchronously, as soon as it is available. If the async attribute is not present but the defer attribute is present, then the script is executed when the page has finished parsing. If neither attribute is present, then the script is fetched and executed immediately, before the user agent continues parsing the page.
簡單的概括:
規範裏沒有提到兩種屬性都有時的效果,但這是文檔中被容許的。這樣的具體效果會在後面討論。
docuemnt.write容許向打開的文檔流中寫入文檔內容;內嵌到HTML裏面的docuemnt.write能夠就地添加文檔內容。考慮到docuemnt.write寫入script標籤的狀況9:
<!-- HTML code --> <script src="https://snipt.net/raw/7b08744009c450e07c0bfc1d606fc72e/"></script> <script>document.write("\<script src=https://snipt.net/raw/a2e8c05c1f6fc0e47d259aa899304e89 \/\>\<\/script\>");</script> <script src="https://snipt.net/raw/4fab3017d3d46cbfc4bbd88aab006650/"></script>
觀察到執行順序和普通的script標籤沒有區別。即便你插入的標籤帶有async或defer,其行爲也是沒有區別的。
讓人糾結的是反過來10使用。因爲第二個腳本是經過document.write
寫入的。被延遲的腳本在執行時,document已經關閉,document.write
是沒有任何效果的。因此,無論使用defer仍是async,第二個腳本始終沒有運行。
原文:
http://pij.robinqu.me/Browser_Scripting/Document_Loading/ScriptTag.html