瀏覽器環境下JavaScript指令碼載入與執行探析之動態指令碼與Ajax指令碼注入_javascript技巧

來源:互聯網
上載者:User

在《瀏覽器環境下JavaScript指令碼載入與執行探析之defer與async特性》中,我們研究了延遲指令碼(defer)和非同步指令碼(async)的執行時機、瀏覽器支援情況、瀏覽器bug以及其他的細節問題。而除了defer和async特性,動態指令碼和Ajax指令碼注入也是兩種常用的建立無阻塞指令碼的方法。總的來看,這兩種方法都能達到指令碼載入不影響頁面解析和渲染的作用,但是在不同的瀏覽器中,這兩種技術所建立的指令碼的執行時機還是有一定差異,今天我們再來探討一下通過動態指令碼技術和Ajax注入的指令碼在這些方面的特性。

代碼準備:

我們使用《瀏覽器環境下JavaScript指令碼載入與執行探析之代碼執行順序》2.3節中的loadScript函數來添加動態指令碼,同時使用這篇文章2.4節中的loadXhrScript函數來實現Ajax指令碼注入。我們把這兩個函數都放在util.js中。

另外,本文使用的CHROME的版本為47.0.2526.80,firefox的版本為43.0.4,opera版本為30.0.1835.125。

1 動態指令碼

1.1動態指令碼的執行時機問題

我們在《瀏覽器環境下JavaScript指令碼載入與執行探析之defer與async特性》中2.3節DEMO的基礎上,增加三個外部js檔案:

dynamic1.js

test += "我是head外部動態指令碼\n";

dynamic2.js

test += "我是body外部動態指令碼\n";

dynamic3.js

test += "我是底部外部動態指令碼\n";

1.1.1 DEMO1:動態指令碼的執行時機初探

HTML的代碼為:

<!DOCTYPE html><html><head><meta charset="UTF-"/><title>Dynamic Script Test</title><script src="http://lib.sinaapp.com/js/jquery/../jquery-...min.js"></script><script src="util.js"></script><script type="text/javascript">var test = "";</script><script>loadScript("dynamic.js");</script><script>test += "我是head內部指令碼\n";</script><script src=".js" type="text/javascript"></script></head><body><button id="test">點擊一下</button><script>loadScript("dynamic.js");</script><script src=".js" type="text/javascript"></script></body><script>loadScript("dynamic.js");</script><script src=".js" type="text/javascript"></script><script>$(function(){test += "我是DOMContentLoaded裡面的指令碼\n";})window.onload = function(){test += "我是window.onload裡面的指令碼\n";var button = document.getElementById("test");button.onclick = function(){console.log(test);}}</script> 

在代碼中,我們先後將3個動態指令檔加入到HTML的<head>標籤中,同時通過與正常外部指令碼和內部指令碼的執行來進行比較,我們看一下不同瀏覽器中的執行結果:

註:firefox和opera中執行結果可能會變化

從上面的例子中,我們可以看出,在不同瀏覽器中,動態指令碼的執行時機差異還是比較大的,但以下兩點是可以明確的:

[1]動態指令碼確實可以起到不阻塞後續指令碼的作用,即延遲作用,但是這個延遲作用卻不一定能夠持續到所有的正常指令碼都執行完畢之後,也無法保證能夠延遲到DOMContentLoaded之後

[2]動態指令碼執行的先後順序是無法保證的,這一點在http://www.jb51.net/article/77920.htm這篇文章以及後續的幾篇文章中進行了詳細的解釋

從這個DEMO中還可以看出,動態指令碼的執行時機具有較大的不確定性,雖然在DEMO1中,動態指令碼都在DOMContentLoaded事件之後執行,但卻也並不意味著動態指令碼就不會阻塞DOMContentLoaded,我們通過DEOM2來看一下:

1.1.2 DEMO2:動態指令碼對DOMContentLoaded的阻塞

我們把DEMO1中的第27行內碼修改為:

<script src="/delayfile.php?url=http://localhost/js/load/3.js&delay=5" type="text/javascript"></script> 

我們利用《瀏覽器環境下JavaScript指令碼載入與執行探析之代碼執行順序》中的delayfile.php,將3.js的返回延遲5秒鐘,我們知道,如果是defer延遲指令碼,無論正常外部指令碼延遲了多長時間,defer指令碼還是會在正常外部指令碼之後執行的,但是動態指令碼卻不是這樣了,我們看一下修改後的代碼在瀏覽器中的執行順序:


註:firefox和opera中執行結果可能會變化

可以看到,在某個正常指令碼載入時間較長的時候,動態指令碼的執行明顯提前,無論在IE還是CHROME、firefox和opera中,均在DOMContentLoaded之前執行,因此我們可以初步判斷,動態指令碼的執行時機是不確定的,在不同瀏覽器的執行時機也都存在差異,但總的來看應該是在代碼載入結束之後,並且線程中沒有JavaScript代碼執行的某個時間,但不同瀏覽器對這個時間有著不同的把握。

因此,動態指令碼是否會阻塞DOMContentLoaded也是不確定的,因為動態指令碼可能在DOMContentLoaded觸發之前,也可能在觸發之後執行。而且由於IE<=8不支援真正的DOMContentLoaded事件,jQuery在IE<=8中也是類比判斷該事件的發生(下一篇會專門講解DOMContentLoaded事件),一定程度上也會對我們上述代碼的執行結果造成影響。

1.1.3 DEMO3:動態指令碼與defer

我們知道,defer指令碼是有著相對明確的執行時機的,即頁面解析完成之後,DOMContentLoaded觸發之前載入並且執行,事實上,二者之間在執行時機上並不存在什麼關聯,但是在實際實驗中發現,動態指令碼可能會在defer指令碼之前或者之後執行,但卻不會打斷defer指令碼的執行,我們再引入《瀏覽器環境下JavaScript指令碼載入與執行探析之defer與async特性》中2.3節的DEMO中的defer指令碼,修改HTML代碼如下:

<!DOCTYPE html><html><head><meta charset="UTF-"/><title>Dynamic Script Test</title><script src="http://lib.sinaapp.com/js/jquery/../jquery-...min.js"></script><script src="util.js"></script><script>$(function(){test += "我是DOMContentLoaded裡面的指令碼\n";})window.onload = function(){test += "我是window.onload裡面的指令碼\n";var button = document.getElementById("test");button.onclick = function(){console.log(test);}}</script><script type="text/javascript">var test = "";</script><script>loadScript("dynamic.js");</script><script>test += "我是head內部指令碼\n";</script><script src="defer.js" type="text/javascript" defer="defer"></script><script src=".js" type="text/javascript"></script></head><body><button id="test">點擊一下</button><script>loadScript("dynamic.js");</script><script src="defer.js" type="text/javascript" defer="defer"></script><script src=".js" type="text/javascript"></script></body><script>loadScript("dynamic.js");</script><script src="defer.js" type="text/javascript" defer="defer"></script><script src=".js" type="text/javascript"></script></html> 

註:firefox和opera中執行結果可能會變化

我們增加了幾個defer的指令碼,再來看一下各個瀏覽器中的執行結果:

從實驗結果可以看出,動態指令碼的執行時機與defer指令碼並沒有直接的關係,表面上看起來在CHROME和firefox中,延遲指令碼總是在動態指令碼之前執行,在《前端最佳化-Javascript篇(2.非同步載入指令碼)》一文中提到過“ScriptDOM和defer同時都可以執行,在不同瀏覽器中它們的優先順序的不一樣的。在Firfox和Chrome中,ScriptDOM的優先順序比defer低,而在IE中情況則相反。”,其實這種優先順序應該是不存在的,我們只需要將defer指令碼加一個載入延遲,那麼動態指令碼的執行就會先於defer指令碼了。

1.2 動態指令碼執行問題總結

我們再來總結一下動態指令碼的執行問題:

[1]首先,動態指令碼確實能夠在一定程度上起到延遲指令碼執行的作用,但由於動態指令碼的執行時機的不確定性,這種延遲作用的效果也是未知的。

[2]其次,動態指令碼的執行順序不一定會按照添加的順序,這是動態指令碼技術比較大的問題之一,最簡單的解決方式就是使用回呼函數,監聽指令碼的載入狀態,在一個指令碼載入結束後再動態添加下一個指令碼。

[3]動態指令碼沒有確切的執行時機,當通過DOM的appendChild、insertBefore等方法將script元素添加到DOM中時,就會去載入JS指令碼,指令碼的執行應該是在載入結束後的某個時機,不同瀏覽器對這個時機的處理差異比較大,比如在IE中,應該是採取儘快執行的策略,也就是在載入結束後儘快尋找時機執行代碼

[4]動態指令碼可能會在DOMContentLoaded觸發之前或者之後執行,因此無法確定其是否會阻塞DOMContentLoaded。而在一般情況下,動態指令碼都會阻塞window.onload,但是也會存在動態指令碼在window.onload觸發之後執行,從而不會阻塞window.onload

2 Ajax注入指令碼

2.1Ajax注入指令碼的執行時機問題

Ajax指令碼注入技術有兩種模式:同步載入和非同步載入,同步載入的情況比較簡單,指令碼的載入和執行會阻塞後面代碼的執行,直到注入的代碼被載入和執行完畢。我們主要討論非同步模式下的情況:

2.1.1 DEMO4:Ajax注入指令碼的執行問題初探

我們再添加3個外部檔案:

ajax1.js

test += "我是head外部AJAX指令碼\n";

ajax2.js

test += "我是body外部AJAX指令碼\n";

ajax3.js

test += "我是底部外部AJAX指令碼\n";

HTML的代碼為:

<!DOCTYPE html><html><head><meta charset="UTF-"/><title>Ajax Script Test</title><script src="http://lib.sinaapp.com/js/jquery/../jquery-...min.js"></script><script src="util.js"></script><script type="text/javascript">var test = "";</script><script>$(function(){test += "我是DOMContentLoaded裡面的指令碼\n";})window.onload = function(){test += "我是window.onload裡面的指令碼\n";var button = document.getElementById("test");button.onclick = function(){console.log(test);}}</script><script>loadXhrScript("ajax.js",true);</script><script>test += "我是head內部指令碼\n";</script><script src=".js" type="text/javascript"></script></head><body><button id="test">點擊一下</button><script>loadXhrScript("ajax.js",true);</script><script src=".js" type="text/javascript"></script></body><script>loadXhrScript("ajax.js",true);</script><script src=".js" type="text/javascript"></script></html> 

在這段代碼中,我們分別在<head>標籤內部、<body>標籤內部、<body>標籤外部共添加了3個注入指令碼,通過正常引入的指令碼作為參照,我們看一下在瀏覽器中的執行結果:

註:firefox、opera、IE中的執行結果可能會變化

從這個執行結果中,我們就可以看到,Ajax注入指令碼的執行時機具有更大的不確定性,事實上,與動態指令碼類似,Ajax注入指令碼的載入過程也是非同步,因此,完成載入的時間首先是不確定的,其次,瀏覽器在指令碼載入完成後何時執行載入的代碼同樣也是不確定的,對於非同步模式下的Ajax注入指令碼的執行時機,我們總結如下:

[1]Ajax注入的指令碼也具有一定的延遲作用,但是與動態指令碼類似,延遲的時間是不確定的。

[2]Ajax注入指令碼的執行順序是無序的,雖然DEMO4中的例子看起來注入的指令碼都是按照添加的順序執行的,但是只要稍微理解非同步以及動態指令碼執行順序問題,就應該能夠明白這一點。

[3]Ajax注入指令碼的執行時機也是不確定的,與指令碼的載入時間以及瀏覽器的處理機制有關。

[4]由於上述的幾點,Ajax注入的指令碼可能會阻塞DOMContentLoaded,也可能會阻塞window.onload。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.