2012-01-25 186 views
194

在html頁面中包含JavaScript的方式有很多種。我知道以下選項:加載並執行腳本的順序

  • 內聯代碼或從包括在<頭>或< body>標籤外部URI
  • 加載[12]
  • 具有無,deferasync屬性(只外部腳本)
  • 包含在靜態源中或由其他腳本動態添加(在不同的解析狀態下,使用不同的方法)

不計算硬盤,javascript:URIs和屬性[3]中的瀏覽器腳本,已經有16種替代方法可以執行JS,我確信我忘記了一些東西。

我並不擔心快速(並行)加載,我對執行順序更加好奇(可能取決於加載順序和document order)。 是否有很好的(跨瀏覽器)參考涵蓋真正的所有情況?例如http://www.websiteoptimization.com/speed/tweak/defer/只處理其中6個,並測試大多數舊瀏覽器。

因爲我擔心沒有,所以我的具體問題是:我有一些(外部)頭文件用於初始化和腳本加載。然後我在本體的最後有兩個靜態的內聯腳本。第一個讓腳本加載器動態地將另一個腳本元素(引用外部js)附加到主體。第二個靜態內聯腳本想要使用添加的外部腳本中的js。它可以依靠另一個被執行(以及爲什麼:-)?

+0

你有沒有看[加載腳本而不阻塞(https://www.stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/)由史蒂夫Souders的?現在有點過時了,但是在給定特定腳本加載技術的情況下,仍然包含瀏覽器行爲的一些有價值的見解。 –

回答

239

如果您不是動態加載腳本或將它們標記爲延遲或異步,則會按頁面中遇到的順序加載腳本。無論它是外部腳本還是內聯腳本都無關緊要 - 它們按頁面中遇到的順序執行。在外部腳本之後的內聯腳本一直保留,直到所有外部腳本加載並運行。

異步腳本(無論它們如何指定爲異步)加載並以不可預知的順序運行。瀏覽器並行加載它們,並且可以按照它想要的順序自由運行它們。

在多個異步事件中沒有可預測的順序。如果需要一個可預測的順序,那麼它將不得不通過註冊來自異步腳本的加載通知進行編碼,並在加載適當的東西時手動排序JavaScript調用。

當動態插入腳本標記時,執行順序的行爲方式取決於瀏覽器。您可以看到Firefox在this reference article中的行爲。簡而言之,較新版本的Firefox會默認動態添加腳本標記以進行異步操作,除非腳本標記已被設置。

帶有async的腳本標記可能會在加載後立即運行。事實上,瀏覽器可能會暫停解析器,並執行該腳本。所以,它幾乎可以在任何時候運行。如果腳本被緩存了,它可能立即運行。如果腳本需要一段時間才能加載,則可能在解析器完成後運行。 async要記住的一點是,它可以隨時運行,而且這個時間是不可預測的。

一個帶有defer的腳本標籤會一直等到整個解析器完成,然後以遇到的順序運行標有defer的所有腳本。這允許您標記幾個相互依賴的腳本,如defer。它們都會被推遲到文檔解析器完成之後,但它們將按照遇到它們的依賴關係的順序執行。我認爲defer就像腳本被放入隊列中一樣,解析器完成後將處理它。從技術上講,瀏覽器可能會隨時在後臺下載腳本,但直到解析器完成解析頁面並解析並運行任何未標記爲延遲或異步的內聯腳本後,它們纔會執行或阻止解析器。

下面是這篇文章報價:

腳本插入腳本在IE和WebKit異步執行,但 同步在Opera和4.0版本之前的Firefox瀏覽器。

HTML5規範的相關部分(適用於更新的兼容瀏覽器)爲here。那裏寫了很多關於異步行爲的內容。顯然,這個規範並不適用於那些您可能需要測試才能確定的行爲的舊瀏覽器(或不確定的瀏覽器)。

從HTML5規範中的一個報價:

然後,第一的描述的情況 下列選項必須遵循:

如果該元素具有src屬性和元素具有延遲 屬性,並且該元素已被標記爲「解析器插入」,並且該元素不具有異步屬性 該元素必須被添加 到腳本列表的末尾t當文檔 完成與解析器的文檔相關聯的解析, 創建該元素時,帽子將執行。

,關於任務隊列的網絡任務源的地方,一旦 的取算法已完成必須設置元素的「準備 是解析器執行的」標誌的任務。解析器將處理執行腳本。

如果元件具有src屬性,並且元件已被標記 爲「解析器插入」,而元件不具有一個異步屬性 元素是文檔的待決解析阻擋腳本 創建元素的解析器。 (只能有每個文檔的一個這樣的 腳本在同一時間。)

,一旦 的取算法已經完成了任務隊列的網絡任務源場所必須設置元素的「準備 是parser-任務執行「的標誌。解析器將處理執行腳本。

如果元件不具有src屬性,並且元件已 標記爲「解析器插入」,並且HTML解析器或 XML解析器創建的腳本元素的文檔具有樣式表是 阻止腳本該元素是 創建元素的解析器的Document的暫掛解析阻止腳本。 (一次只能有 爲每個文檔一個這樣的腳本。)

設置元素的「準備好解析器執行」標誌。解析器將會執行腳本 。

如果該元素具有src屬性,不具備異步特性, 和不具備「強制異步」標誌設置元素必須添加 到腳本,將列表的末尾按照儘可能快的 的順序執行與腳本元素的文檔相關聯的 時刻準備腳本算法開始。

,關於任務隊列的網絡任務源的地方,一旦 的取算法已經完成了必須執行下列步驟的任務:

如果元素是不是現在在腳本 列表中的第一個元素將盡快按順序執行,將 添加到上面,然後將該元素標記爲準備好,但會中止這些步驟,而不執行 執行腳本。

執行:執行該腳本列表中的第一個腳本 元素所對應的腳本塊,該腳本將盡快按照 的順序執行。

從此腳本列表中刪除第一個元素,它將盡快按順序執行 。

如果將爲了儘快執行儘可能 仍然不爲空,首先進入已經被標記爲 準備腳本的清單,然後跳回一步標記執行。

如果該元素具有src屬性元素必須被添加到 組將在時儘快腳本元素的文檔 的執行編寫一個腳本算法 啓動腳本。

的任務在任務隊列,一旦 的取算法已經完成了必須執行的腳本塊和 聯網任務源的地方,然後從組腳本,將立即執行 儘可能刪除元素。

否則即使其他腳本已經在執行,用戶代理也必須立即執行腳本塊 。

+0

感謝您的回答,但問題是腳本*是*動態添加到頁面,這意味着[它被認爲是異步(http://stackoverflow.com/questions/3408805/is-the-async-屬性屬性-有用-IF-A-腳本是動態地添加到所述)。或者這隻在工作?我的經驗也是他們按文件順序執行? – Bergi

+0

@Bergi - 如果它是動態添加的,那麼它是異步的,執行順序是不確定的,除非你編寫代碼來控制它。 – jfriend00

+0

只是,[Kolink狀態(http://stackoverflow.com/a/8996905/1048572)對面... – Bergi

11

瀏覽器將按照發現它們的順序執行腳本。如果您調用外部腳本,它將阻止頁面,直到腳本被加載並執行。

爲了驗證這一事實:

// file: test.php 
sleep(10); 
die("alert('Done!');"); 

// HTML file: 
<script type="text/javascript" src="test.php"></script> 

動態添加腳本,儘快爲他們被附加到文件執行。

爲了驗證這一事實: - 「你好!」

<!DOCTYPE HTML> 
<html> 
<head> 
    <title>Test</title> 
</head> 
<body> 
    <script type="text/javascript"> 
     var s = document.createElement('script'); 
     s.type = "text/javascript"; 
     s.src = "link.js"; // file contains alert("hello!"); 
     document.body.appendChild(s); 
     alert("appended"); 
    </script> 
    <script type="text/javascript"> 
     alert("final"); 
    </script> 
</body> 
</html> 

警報的順序是 「追加」> - >「final」

如果在腳本中嘗試訪問尚未到達的元素(例如:<script>do something with #blah</script><div id="blah"></div>),則會出現錯誤。

總體來說,是的,你可以包括外部腳本,然後訪問他們的函數和變量,但是隻有當你退出當前<script>標籤,並開始一個新的。

+0

我可以確認這種行爲。但是我們的反饋頁面上有提示,它可能只在test.php被緩存時才起作用。你知道關於這個的任何規範/參考鏈接嗎? – Bergi

+4

link.js不阻塞。使用類似於您的php腳本的腳本來模擬較長的下載時間。 – 1983

+10

這個答案是不正確的。 「動態添加的腳本一旦被附加到文檔後就立即執行」並非總是如此。有時候這是真的(例如對於舊版本的Firefox),但通常情況並非如此。正如jfriend00的回答中所述,執行順序不是確定的。 –