[轉]Script標簽和腳本執行順序


Script標簽和腳本執行順序

這里詳細聊聊和script標簽相關的腳本執行順序。

Script標簽的默認行為

幾個首要特性:

  • script標簽(不帶deferasync屬性)的會阻止文檔渲染。相關腳本會立即下載並執行。
  • document.currentScript可以獲得當前正在運行的腳本(Chrome 29+, FF4+)
  • 腳本順序再默認情況下和script標簽出現的順序一致

假設如下簡單代碼1,最終會產生三個alert依次為“A”、“B”、“C”。

<!-- HTML Code -->
<script>alert("A");</script>
<script>alert("B");</script>
<script>alert("C");</script>

我們再考慮有網絡請求的情況2

<!-- 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+)。

async表示該script標簽並不柱塞,也不同步執行。瀏覽器只需要在腳本下載完畢后再執行即可——不必柱塞頁面渲染等待該腳本的下載和執行。

如下代碼4,會得到三個alert,但是alert的內容分別是”A”,”C”,”B”。

<!-- 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並沒有阻止后續文檔解析和腳本執行。

考究這個屬性產生的原有,其實有大量的腳本加載器在做這樣的事情:

var script = document.createElement("script");
script.src = "file.js";
document.body.appendChild(script);

不難想象,通過腳本異步插入的script標簽達到的效果和帶async屬性的script標簽是一樣的。換句話說,由腳本插入的script標簽默認是async的。

另外,對內聯腳本設置async屬性是沒有意義的,也不產生其他效果。其包含的腳本總是立即執行的。

defer屬性

帶有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屬性。

async與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.

簡單的歸納:

  • 僅有async屬性,腳本會異步執行
  • 僅有defer屬性,腳本會在文檔解析完畢后執行
  • 兩個屬性都沒有,腳本會被同步下載並執行,期間會柱塞文檔解析

規范里沒有提到兩種屬性都有時的效果,但這是文檔中被允許的。這樣的具體效果會在后面討論。

document.write的影響

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



猜您在找 html解析和渲染過程 與 Script標簽和腳本執行順序 unity 腳本執行順序設置 Script Execution Order Settings [轉]shell腳本每行的執行順序是怎樣 script標簽加載順序(defer & async) js 動態創建標簽 並執行其標簽中的內容 script放置最佳位置以及html執行順序 document.createElement('script') 和 DOMContentLoaded 執行順序 script標簽引入腳本的引入位置與效果 面試小記---外部腳本必須包含