標籤:
javaScript檔案(下面簡稱指令檔)需要被HTML檔案引用才能在瀏覽器中運行。在HTML檔案中可以通過不同的方式來引用指令檔,我們需要關注的是,這些方式的具體實現和這些方式可能會帶來的效能問題。
當瀏覽器遇到(內嵌)<script>標籤時,當前瀏覽器無從獲知javaScript是否會修改頁面內容。因此,這時瀏覽器會停止處理頁面,先執行javaScript代碼,然後再繼續解析和渲染頁面。同樣的情況也發生在使用 src 屬性加在javaScript的過程中(即外鏈 javaScript),瀏覽器必須先花時間下載外鏈檔案中的代碼,然後解析並執行它。在這個過程中,頁面渲染和使用者互動完全被阻塞了。
也就是說:每當瀏覽器解析到<script>標籤(無論內嵌還是外鏈)時,瀏覽器會(一根筋地)優先下載、解析並執行該標籤中的javaScript代碼,而阻塞了其後所有頁面內容的下載和渲染。
五種引用指令碼的方式:
1.慣例的做法
2.經典的做法
3.動態指令碼
4.LABjs
5.requireJS
==慣例的做法
最傳統的方式是在head標籤插入入<script>標籤:
然而這種常規的做法卻隱藏著嚴重的效能問題。根據上述對<script>標籤特性的描述,我們知道,在該樣本中,當瀏覽器解析到<script>標籤時,瀏覽器會停止解析其後的內容,而優先下載指令檔,並執行其中的代碼,這意味著,其後的test.css樣式檔案和<body>標籤都無法被載入,由於<body>標籤無法被載入,那麼頁面自然就無法渲染了。因此在該javaScript代碼完全執行完之前,頁面都是一片空白。
<script type="text/javaScript" src="example.js"></script>
==經典的做法
既然<script>標籤會阻塞其後內容的載入,那麼將<script>標籤放到所有頁面內容之後不就可以避免這種糟糕的狀況了嗎? 將所有的<script>標籤儘可能地放到<body>標籤底部,以盡量避免對頁面其餘部分下載的影響。
然在IE8+瀏覽器上已經實現了指令碼並行下載,但在某些瀏覽器中(即使指令檔放到了<body>標籤底部),頁面中指令碼仍是一個接著一個載入的。所以我們需要下一個方法,即:動態載入指令碼
==動態指令碼
通過文件物件模型(DOM),我們可以幾乎可以頁面任意地方建立
var script=document.createElement(‘script’);
script.type=’text/javaScript’;
script.src=’file1.js’;
document.getElementsByTagName(‘head’)[0].appendChild(script);
上述代碼動態建立了一個外鏈file1的<script>標籤,並將其添加到<head>標籤內。這種技術的重點在於:
無論在何時啟動下載,檔案的下載和執行過程不會阻塞頁面其他進程(包括指令碼載入)。
然而這種方法也是有缺陷的。這種方法載入的指令碼會在下載完成後立即執行,那麼意味著多個指令碼之間的運行順序是無法保證的(除了Firefox和Opera)。當某個指令碼對另一個指令碼有依賴關係時,就很可能發生錯誤了。比如,寫一個jQuery代碼,需要引入jQuery庫,然而你寫的jQuery代碼檔案很可能會先完成下載並立即執行,這時瀏覽器會報錯——‘jQuery未定義’之類的,因為此時jQuery庫還未下載完成。於是做出以下改進:
function loadScript(url,callback){
var script=document.createElement(‘script’);
script.type=”text/javaScript”;
if(script.readyState){//IE
script.onreadystatechange=function(){
if(script.readyState==”loaded”||script.readyState==”complete”){
script.onreadystatechange=null; callback(); } };
}else{//其他瀏覽器
script.onload=function(){ callback(); }; } script.src=url;
document.getElementsByTagName(‘head’)[0].appendChild(script);
}
上述代碼改進的地方就是增加了一個回呼函數,該函數會在相應指令檔載入完成後被調用。這樣便可以實現順序載入了,寫法如下(假設file2依賴file1,file1和file3相互獨立):
loadScript(‘file1.js’,function(){ loadScript(‘file2.js’,function(){}); }); loadScript(‘file3.js’,function(){});
file2會在file1載入完後才開始載入,保證了在file2執行前file1已經準備妥當。而file1和file3是並行下載的,互不影響。 雖然loadScript函數已經足夠好,但還是有些不盡人意的地方——通過分析這段代碼,我們知道,loadScript函數中的順序載入是以指令碼的阻塞載入來實現的(正如上述紅字部分指出的那樣)。而我們真正想實現的是——指令碼同步下載並按相應順序執行,即並行載入並順序執行。
==LABjs
LABjs庫能幫我們真正地實現“並行載入與順序執行”,推薦寫法如下:
<script src="LAB.js"></script>
<script type="text/javaScript">
$LAB
.script("script1.js").wait()
.script("script2-a.js")
.script("script2-b.js")
.wait(function(){
initScript1();
initScript2();
})
.script("script3.js")
.wait(function(){
initScript3();
});
</script>
==requireJS
<script src="require.js"></script>
<script type="text/javaScript">
require([
"script1.js",
"script2-a.js",
"script2-b.js",
"script3.js"
],
function(){
initScript1();
initScript2();
initScript3();
}
);
</script>
js的並行載入與順序執行