動態引入的外部 JS 文件在各瀏覽器中的加載順序不一致問題解決

標準參考

無。

問題描述

頁面開發過程中,爲了避免頁面加載時引入過多外部 JS 文件,導致阻塞頁面內容下載及渲染的情況出現。將會採用頁面內容加載完成後,動態加載外部 JavaScript 文件的方法來解決此類問題。但是,需要注意的是,常用動態插入外部腳本文件的方法在各瀏覽器中的執行順序並不一致。

造成的影響

對於動態插入的 SCRIPT 文件,不能保證在各瀏覽器能阻塞其後腳本的執行。

受影響的瀏覽器

所有瀏覽器  

問題分析

使用 appenChild insertBefore 等方法向文檔中動態插入 SCRIPT 節點後,各瀏覽器中對腳本的執行順序存在差異。

以下例子中均使用腳本代碼插入遠程文件:http://code.jquery.com /jquery-1.4.2.js ( jQuery 源碼 ),該文件中定義了全局變量 $,當遠程腳本文件加載完成後該變量將可用 。

情況1:

<script>
var js = document.createElement("script");
document.getElementsByTagName("head")[0].appendChild(js);
js.src = 'http://code.jquery.com/jquery-1.4.2.js';
</script>

<script type="text/javascript">
alert($)
</script>

代碼首先創建 SCRIPT 標記並插入到 HEAD 標記中,再將 SCRIPT 的 src 屬性指向外部 JS 文件,由之後的 SCRIPT 標記中代碼調用外部程序全局變量。

各瀏覽器表現及分析如下:

IE Firefox Opera 彈出($)函數體。此方法中動態附加進文檔的 js 文件會阻斷下一個 SCRIPT 標記內的代碼解析,直至它全部解析完。
Chrome Safari 腳本出錯,"$ is not defined"。此方法中動態附加進文檔的 js 文件不會阻斷下一個 SCRIPT 標記內的代碼解析。

情況2:

<script type="text/javascript">
var js = document.createElement("script");
js.src = 'http://code.jquery.com/jquery-1.4.2.js';
document.getElementsByTagName("head")[0].appendChild(js);
</script>

<script type="text/javascript">
alert($)
</script>

代碼首先創建 SCRIPT 標記,將 src 屬性指向外部 JS 文件。最後插入到 HEAD 標記中,由之後的 SCRIPT 標記中代碼調用外部程序全局變量。

各瀏覽器表現及分析如下:

Firefox Opera 彈出($)函數體。此方法中動態附加進文檔的 js 文件會阻斷下一個 SCRIPT 標記內的代碼解析,直至它全部解析完。
IE Chrome Safari 腳本出錯,"$ is not defined"。此方法中動態附加進文檔的 js 文件不會阻斷下一個 SCRIPT 標記內的代碼解析。

情況3:

<script type="text/javascript">
var js = document.createElement("script");
document.getElementsByTagName("head")[0].appendChild(js);
js.src = 'http://code.jquery.com/jquery-1.4.2.js';
alert($)
</script>

代碼首先創建 SCRIPT 標記,將 src 屬性指向外部 JS 文件。最後插入到 HEAD 標記中,其後代碼立即調用外部程序全局變量。

各瀏覽器表現及分析如下:

所有瀏覽器 腳本出錯,"$ is not defined"。此方法中動態附加進文檔的 js 文件不會阻斷同一個 SCRIPT 標記內的代碼解析。

情況4:

<script id="a"></script>

<script type="text/javascript">
var a=document.getElementById('a');
a.src = 'http://code.jquery.com/jquery-1.4.2.js';
alert($)
</script>  

代碼獲取某個 SCRIPT 標記的引用,在將其 src 屬性指向外部 JS 文件,其後代碼立即調用外部程序全局變量。

各瀏覽器表現及分析如下:

所有瀏覽器 腳本出錯,"$ is not defined"。此方法中動態附加進文檔的 js 文件不會阻斷同一個 SCRIPT 標記內的代碼解析。

情況5:

<script id="a" ></script>

<script type="text/javascript">
var a=document.getElementById('a');
a.src = 'http://code.jquery.com/jquery-1.4.2.js';
</script>

<script type="text/javascript">
alert($)
</script>

代碼獲取某個 SCRIPT 標記的引用,在將其 src 屬性值變更爲外部 JS 文件,由之後的 SCRIPT 標記中代碼調用外部程序全局變量。

各瀏覽器表現及分析如下:

IE Firefox Opera 彈出($)函數體。此方法中動態附加進文檔的 js 文件會阻斷下一個 SCRIPT 標記內的代碼解析,直至它全部解析完。
Chrome Safari 腳本出錯,"$ is not defined"。此方法中動態附加進文檔的 js 文件不會阻斷下一個 SCRIPT 標記內的代碼解析。

綜合以上情況,對於動態插入的 SCRIPT 文件,使用不同的插入方法將有不同的表現,不能保證在各瀏覽器能阻塞其後腳本的執行。

解決方案

對於必須動態附加到文檔的外部 js 文件,要保證動態引入的腳本全部執行完成後,才能執行後續代碼。

可以將此部分代碼封裝後調用,如:

function loadJS(url, success) {
  var domScript = document.createElement('script');
  domScript.src = url;
  success = success || function(){};
  domScript.onload = domScript.onreadystatechange = function() {
    if (!this.readyState || 'loaded' === this.readyState || 'complete' === this.readyState) {
      success();
      this.onload = this.onreadystatechange = null;
      this.parentNode.removeChild(this);
    }
  }
  document.getElementsByTagName('head')[0].appendChild(domScript);
}
//執行加載外部 JS 文件
loadJS('a.js',function (){
   loadJS('b.js',function (){
    loadJS('c.js',function (){
      alert('ok');
    });
   });
});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章