編寫(xiě)高性能的JavaScript 腳本的加載與執(zhí)行
更新時(shí)間:2010年04月19日 18:43:08 作者:
把腳本放在body中,當(dāng)瀏覽器遇見(jiàn)<script>標(biāo)簽時(shí), 瀏覽器不知道腳本會(huì)插入文本還是html標(biāo)簽,因此瀏覽器會(huì)停止分析html頁(yè)面而去執(zhí)行腳本。
腳本可以放在html頁(yè)面的head里面,也可以放在body里面。
把腳本放在body中,當(dāng)瀏覽器遇見(jiàn)<script>標(biāo)簽時(shí), 瀏覽器不知道腳本會(huì)插入文本還是html標(biāo)簽,因此瀏覽器會(huì)停止分析html頁(yè)面而去執(zhí)行腳本。當(dāng)使用src的方式添加腳本時(shí),瀏覽器也會(huì)做同樣的動(dòng)作。在腳本處理的時(shí)候,頁(yè)面呈現(xiàn)和用戶(hù)交互將被完全阻止。腳本下載和執(zhí)行阻塞了其他資源的下載,比如呈現(xiàn)頁(yè)面使用的圖片。(雖然很多瀏覽器實(shí)現(xiàn)了腳本并行下載的技術(shù),但是這個(gè)問(wèn)題依然沒(méi)有解決)
腳本的位置
鑒于上面的理由,腳本應(yīng)該始終放在頁(yè)面的底部,即</body>前面。
一個(gè)簡(jiǎn)單的示例:
<html>
<head>
<title>Script Example</title>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<p>Hello world!</p>
<script type="text/javascript" src="file1.js"></script>
<script type="text/javascript" src="file2.js"></script>
<script type="text/javascript" src="file3.js"></script>
</body>
</html>
合并腳本
因?yàn)槟_本下載阻塞了頁(yè)面呈現(xiàn),因而應(yīng)該減少頁(yè)面<script>標(biāo)簽的使用,不管腳本是內(nèi)聯(lián)的還是外部的。在處理外部腳本的時(shí)候情況比較特殊,瀏覽器下載一個(gè)100kb的腳本的時(shí)間將遠(yuǎn)遠(yuǎn)小于4個(gè)25kb的腳本,因?yàn)榻⒁粋€(gè)請(qǐng)求要消耗大量的時(shí)間。所以頁(yè)面應(yīng)該盡量的減少外部腳本的引用。
非阻塞的腳本
秘訣在于當(dāng)頁(yè)面loading完成之后再來(lái)加載腳本,也就是在window對(duì)象的onload事件觸發(fā)之前 。下面是實(shí)現(xiàn)的幾種方式:
1.使用defer
<html>
<head>
<title>Script Defer Example</title>
</head>
<body>
<script defer>
alert("defer");
</script>
<script>
alert("script");
</script>
<script>
window.onload = function(){
alert("load");
};
</script>
</body>
</html>
頁(yè)面彈出框出現(xiàn)的順序: script/defer/load,這個(gè)技術(shù)的缺點(diǎn)是IE4+和FF3.5+才支持。
非阻塞的腳本(續(xù))
2. 動(dòng)態(tài)腳本元素
要知道<script>和普通的html標(biāo)簽并沒(méi)有本質(zhì)的區(qū)別,所以可以利用標(biāo)準(zhǔn)的DOM方法動(dòng)態(tài)的添加腳本文件引用。方法如下:
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "file1.js";
document.getElementsByTagName("head")[0].appendChild(script);
當(dāng)這個(gè)標(biāo)簽一旦加入到html中,腳本文件就開(kāi)始下載。這種方法的一個(gè)特點(diǎn)就是,文件下載和執(zhí)行并不阻塞html頁(yè)面其它部分的處理。通常將這樣的腳本放在<head>中較之<body>更加安全,尤其是文件包含的代碼需要在頁(yè)面的load事件中執(zhí)行。如果body的內(nèi)容還沒(méi)有被完全的加載,IE還會(huì)彈出“禁止操作”的錯(cuò)誤。
當(dāng)腳本文件下載完成之后,腳本立即執(zhí)行(FF、Opera會(huì)等待前一個(gè)以同樣方式添加的腳本執(zhí)行)。當(dāng)腳本自執(zhí)行時(shí),這沒(méi)什么問(wèn)題。但是如果腳本包含頁(yè)面中其它腳本使用的interfaces,你需要確認(rèn)腳本已經(jīng)加載完成并且可用。幸好,當(dāng)獲得script標(biāo)簽的src的值之后,F(xiàn)irefox, Opera, Chrome, and Safari 3+ 會(huì)觸發(fā)一個(gè)load事件。
var script = document.createElement("script")
script.type = "text/javascript";
//Firefox, Opera, Chrome, Safari 3+
script.onload = function(){
alert("Script loaded!");
};
script.src = "file1.js";
document.getElementsByTagName("head")[0].appendChild(script);
IE則提供了另外一種解決方案--readystatechange事件。根據(jù)下載
腳本文件所處的狀態(tài),readyState 的值有以下幾種:
"uninitialized"
默認(rèn)狀態(tài)
"loading"
開(kāi)始下載
"loaded"
下載完成
"interactive"
下載完成,但是并非全部可用
"complete"
所有數(shù)據(jù)可用
IE的實(shí)現(xiàn)方式:
var script = document.createElement("script")
script.type = "text/javascript";
script.onreadystatechange = function(){
if (script.readyState == "loaded" || script.readyState == "complete"){
script.onreadystatechange = null;
alert("Script loaded.");
}
};
script.src = "file1.js";
document.getElementsByTagName("head")[0].appendChild(script);
下面是綜合以后的通用方法:
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 { //Others
script.onload = function(){
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
The loadScript() function is used as follows:
loadScript("file1.js", function(){
alert("File is loaded!");
});
現(xiàn)在你可以按這種動(dòng)態(tài)方式加載腳本了,但是你仍然需要考慮這些文件的下載順序。主流瀏覽器中只有FF和Opera保證腳本的執(zhí)行順序和你指定的下載順序一致,其他瀏覽器將按照腳本文件從服務(wù)器返回的順序來(lái)執(zhí)行。雖然如此,我們?nèi)匀挥刑娲慕鉀Q方案:
loadScript("file1.js", function(){
loadScript("file2.js", function(){
loadScript("file3.js", function(){
alert("All files are loaded!");
});
});
});
這樣我們就能保證腳本文件的下載順序嚴(yán)格的按照f(shuō)ile1-file2-file3的方式進(jìn)行。
注明:以上內(nèi)容來(lái)自:<High Performance JavaScript>by Nicholas C. Zakas
把腳本放在body中,當(dāng)瀏覽器遇見(jiàn)<script>標(biāo)簽時(shí), 瀏覽器不知道腳本會(huì)插入文本還是html標(biāo)簽,因此瀏覽器會(huì)停止分析html頁(yè)面而去執(zhí)行腳本。當(dāng)使用src的方式添加腳本時(shí),瀏覽器也會(huì)做同樣的動(dòng)作。在腳本處理的時(shí)候,頁(yè)面呈現(xiàn)和用戶(hù)交互將被完全阻止。腳本下載和執(zhí)行阻塞了其他資源的下載,比如呈現(xiàn)頁(yè)面使用的圖片。(雖然很多瀏覽器實(shí)現(xiàn)了腳本并行下載的技術(shù),但是這個(gè)問(wèn)題依然沒(méi)有解決)
腳本的位置
鑒于上面的理由,腳本應(yīng)該始終放在頁(yè)面的底部,即</body>前面。
一個(gè)簡(jiǎn)單的示例:
復(fù)制代碼 代碼如下:
<html>
<head>
<title>Script Example</title>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<p>Hello world!</p>
<script type="text/javascript" src="file1.js"></script>
<script type="text/javascript" src="file2.js"></script>
<script type="text/javascript" src="file3.js"></script>
</body>
</html>
合并腳本
因?yàn)槟_本下載阻塞了頁(yè)面呈現(xiàn),因而應(yīng)該減少頁(yè)面<script>標(biāo)簽的使用,不管腳本是內(nèi)聯(lián)的還是外部的。在處理外部腳本的時(shí)候情況比較特殊,瀏覽器下載一個(gè)100kb的腳本的時(shí)間將遠(yuǎn)遠(yuǎn)小于4個(gè)25kb的腳本,因?yàn)榻⒁粋€(gè)請(qǐng)求要消耗大量的時(shí)間。所以頁(yè)面應(yīng)該盡量的減少外部腳本的引用。
非阻塞的腳本
秘訣在于當(dāng)頁(yè)面loading完成之后再來(lái)加載腳本,也就是在window對(duì)象的onload事件觸發(fā)之前 。下面是實(shí)現(xiàn)的幾種方式:
1.使用defer
復(fù)制代碼 代碼如下:
<html>
<head>
<title>Script Defer Example</title>
</head>
<body>
<script defer>
alert("defer");
</script>
<script>
alert("script");
</script>
<script>
window.onload = function(){
alert("load");
};
</script>
</body>
</html>
頁(yè)面彈出框出現(xiàn)的順序: script/defer/load,這個(gè)技術(shù)的缺點(diǎn)是IE4+和FF3.5+才支持。
非阻塞的腳本(續(xù))
2. 動(dòng)態(tài)腳本元素
要知道<script>和普通的html標(biāo)簽并沒(méi)有本質(zhì)的區(qū)別,所以可以利用標(biāo)準(zhǔn)的DOM方法動(dòng)態(tài)的添加腳本文件引用。方法如下:
復(fù)制代碼 代碼如下:
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "file1.js";
document.getElementsByTagName("head")[0].appendChild(script);
當(dāng)這個(gè)標(biāo)簽一旦加入到html中,腳本文件就開(kāi)始下載。這種方法的一個(gè)特點(diǎn)就是,文件下載和執(zhí)行并不阻塞html頁(yè)面其它部分的處理。通常將這樣的腳本放在<head>中較之<body>更加安全,尤其是文件包含的代碼需要在頁(yè)面的load事件中執(zhí)行。如果body的內(nèi)容還沒(méi)有被完全的加載,IE還會(huì)彈出“禁止操作”的錯(cuò)誤。
當(dāng)腳本文件下載完成之后,腳本立即執(zhí)行(FF、Opera會(huì)等待前一個(gè)以同樣方式添加的腳本執(zhí)行)。當(dāng)腳本自執(zhí)行時(shí),這沒(méi)什么問(wèn)題。但是如果腳本包含頁(yè)面中其它腳本使用的interfaces,你需要確認(rèn)腳本已經(jīng)加載完成并且可用。幸好,當(dāng)獲得script標(biāo)簽的src的值之后,F(xiàn)irefox, Opera, Chrome, and Safari 3+ 會(huì)觸發(fā)一個(gè)load事件。
復(fù)制代碼 代碼如下:
var script = document.createElement("script")
script.type = "text/javascript";
//Firefox, Opera, Chrome, Safari 3+
script.onload = function(){
alert("Script loaded!");
};
script.src = "file1.js";
document.getElementsByTagName("head")[0].appendChild(script);
IE則提供了另外一種解決方案--readystatechange事件。根據(jù)下載
腳本文件所處的狀態(tài),readyState 的值有以下幾種:
"uninitialized"
默認(rèn)狀態(tài)
"loading"
開(kāi)始下載
"loaded"
下載完成
"interactive"
下載完成,但是并非全部可用
"complete"
所有數(shù)據(jù)可用
IE的實(shí)現(xiàn)方式:
復(fù)制代碼 代碼如下:
var script = document.createElement("script")
script.type = "text/javascript";
script.onreadystatechange = function(){
if (script.readyState == "loaded" || script.readyState == "complete"){
script.onreadystatechange = null;
alert("Script loaded.");
}
};
script.src = "file1.js";
document.getElementsByTagName("head")[0].appendChild(script);
下面是綜合以后的通用方法:
復(fù)制代碼 代碼如下:
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 { //Others
script.onload = function(){
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
The loadScript() function is used as follows:
loadScript("file1.js", function(){
alert("File is loaded!");
});
現(xiàn)在你可以按這種動(dòng)態(tài)方式加載腳本了,但是你仍然需要考慮這些文件的下載順序。主流瀏覽器中只有FF和Opera保證腳本的執(zhí)行順序和你指定的下載順序一致,其他瀏覽器將按照腳本文件從服務(wù)器返回的順序來(lái)執(zhí)行。雖然如此,我們?nèi)匀挥刑娲慕鉀Q方案:
復(fù)制代碼 代碼如下:
loadScript("file1.js", function(){
loadScript("file2.js", function(){
loadScript("file3.js", function(){
alert("All files are loaded!");
});
});
});
這樣我們就能保證腳本文件的下載順序嚴(yán)格的按照f(shuō)ile1-file2-file3的方式進(jìn)行。
注明:以上內(nèi)容來(lái)自:<High Performance JavaScript>by Nicholas C. Zakas
相關(guān)文章
簡(jiǎn)單了解JavaScript操作XPath的一些基本方法
XPath構(gòu)建于XML之上,以表示路徑的方式來(lái)確定XML中元素位置,事實(shí)上并不是太常用,這里我們來(lái)簡(jiǎn)單了解JavaScript操作XPath的一些基本方法2016-06-06JS實(shí)現(xiàn)基本的網(wǎng)頁(yè)計(jì)算器功能示例
這篇文章主要介紹了JS實(shí)現(xiàn)基本的網(wǎng)頁(yè)計(jì)算器功能,涉及JavaScript事件響應(yīng)及數(shù)值運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下2020-01-01PHP實(shí)現(xiàn)的各種中文編碼轉(zhuǎn)換類(lèi)分享
這篇文章主要介紹了PHP實(shí)現(xiàn)的各種中文編碼轉(zhuǎn)換類(lèi)分享,本文類(lèi)庫(kù)支持簡(jiǎn)體中文、繁體中文、GB2312、BIG5、UTF-8等多種格式之間的轉(zhuǎn)換,需要的朋友可以參考下2015-01-01Avalonjs雙向數(shù)據(jù)綁定與監(jiān)聽(tīng)的實(shí)例代碼
本文通過(guò)實(shí)例代碼給大家介紹了Avalonjs雙向數(shù)據(jù)綁定與監(jiān)聽(tīng)的實(shí)現(xiàn)代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的的朋友參考下吧2017-06-06javascript內(nèi)嵌式與外鏈?zhǔn)降幕緫?yīng)用方式
這篇文章主要介紹了javascript內(nèi)嵌式與外鏈?zhǔn)降幕緫?yīng)用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12Layer組件多個(gè)iframe彈出層打開(kāi)與關(guān)閉及參數(shù)傳遞的方法
今天小編就為大家分享一篇Layer組件多個(gè)iframe彈出層打開(kāi)與關(guān)閉及參數(shù)傳遞的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-09-09JS使用setInterval計(jì)時(shí)器實(shí)現(xiàn)挑戰(zhàn)10秒
這篇文章主要為大家詳細(xì)介紹了JS使用setInterval計(jì)時(shí)器實(shí)現(xiàn)挑戰(zhàn)10秒,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11