1. 程式人生 > >動態引入的外部 JS 檔案在各瀏覽器中的載入順序不一致問題解決

動態引入的外部 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');
    });
   });
});