原則上來說,HTML在使用<script>
標(biāo)簽加載外部腳本文件時捐韩,會順序下載退唠,順序執(zhí)行,并阻礙其他資源文件的下載荤胁,比如圖片(當(dāng)然瞧预,如今主流瀏覽器是可以實現(xiàn)JS和CSS文件并行下載)。
(在Chrome下測試仅政,三張圖片只會有兩張被阻塞垢油。我猜測,Chrome是想做某些優(yōu)化的已旧,但是秸苗,顯然優(yōu)化的不夠徹底召娜。不同瀏覽器表現(xiàn)還是不一致的)
為了加速頁面渲染运褪,不讓腳本文件阻塞其他資源下載,可以考慮“異步加載腳本”的技術(shù)。
后續(xù)的測試都基于Chrome瀏覽器(版本56.0.2924.87)秸讹。
1. Script DOM Element
這恐怕是最常見的異步加載腳本方法檀咙,即,動態(tài)創(chuàng)建一個script
標(biāo)簽璃诀,并設(shè)置其src
值弧可。如下:
function createScript(url){
var scrElem = document.createElement('script');
srcElem.src = url;
document.getElementsByTagName('head')[0].appendChild(scrElem);
}
用createScript
方式加載JS文件,不會阻塞下載其他資源劣欢。
但是這種方式會阻塞window.onload事件棕诵,參考chrome developer timeline:
優(yōu)點::
- 支持跨域加載腳本文件
- 兼容性最好、普適性最高的方案
缺點::
- 腳本無序執(zhí)行
- 會阻塞onload事件
2. XMLHttpRequest
通過XMLHttpRequest
的方式下載腳本文件凿将,然后使用eval
或者動態(tài)添加<script>
標(biāo)簽并設(shè)置其text
屬性來執(zhí)行腳本校套。
// 不考慮IE
var xhrObj = new XMLHttpRequest();
xhrObj .onreadystatechange = function(){
if (xhrObj .readyState == 4) {
eval(xhrObj.responseText);
或者
var scrElem = document.createElement('script');
srcElem.text= xhrObj.responseText;
document.getElementsByTagName('head')[0].appendChild(scrElem);
}
}
xhrObj .open('GET', 'a.js', true);
xhrObj .send('');
稍微修改下上面的例子:
index.html
<html>
...
<body>
<script type="application/javascript" src="js1.js"></script>
</body>
</html>
js1.js
var xhrObj = new XMLHttpRequest();
xhrObj .onreadystatechange = function(){
if (xhrObj .readyState == 4) {
var scrElem = document.createElement('script');
srcElem.text= xhrObj.responseText;
document.getElementsByTagName('head')[0].appendChild(scrElem);
}
};
xhrObj .open('GET', 'js2.js', true);
xhrObj .send('');
查看chrome developer timeline:
優(yōu)點::
- 將腳本下載和腳本執(zhí)行分離開,可以在適當(dāng)?shù)臅r候再執(zhí)行腳本牧抵。
- 不會阻塞onload事件
缺點::
- 通過XMLHttpRequest獲取的腳本文件必須和主頁面是同一個域名下笛匙。也就是說,不支持跨域下載腳本犀变。因此不適合加載第三方文件妹孙。
- 腳本無序執(zhí)行。
3. Script defer和async
兩者都支持異步加載文件获枝,不同之處是蠢正,defer
會在全部資源下載完畢后才執(zhí)行JS文件;async
在腳本文件下載完就立刻執(zhí)行映琳,并且机隙,async
模式加載的JS文件無法依序執(zhí)行,對于有順序依賴的腳本來說萨西,不應(yīng)該采用這種方式有鹿。defer
相對友好一些,并可以保證JS文件按照順序執(zhí)行谎脯。
稍微對程序做些修改:
優(yōu)點::
- defer和async優(yōu)點:支持跨域加載腳本文件葱跋。
- defer優(yōu)點:可以保證JS文件按照順序執(zhí)行。
缺點::
- defer和async缺點:IE10以上(包括IE10)才支持源梭。
- async缺點:JS文件無法依序執(zhí)行娱俺。
- 會阻塞onload事件
4. Script in Iframe
創(chuàng)建了一個隱藏的iframe標(biāo)簽,設(shè)置其src值為JS代碼废麻,然后插入到主頁面中荠卷。
這種方式在實際項目中很少用到,因為iframe是開銷最高的DOM元素烛愧。常用場景是顯示廣告(廣告一般需要運行在隔離環(huán)境中油宜,iframe很合適)掂碱。
<iframe src=“a.html” frameborder="1" name="rightFrame" id="rightFrame"></iframe>
注意,src的值是a.html慎冤,而不是a.js疼燥,因為iframe默認(rèn)其返回值為HTML文檔。所以需要在HTML文檔中把外部腳本轉(zhuǎn)成行內(nèi)腳本蚁堤。
和XMLHttpRequest一樣醉者,iframe不支持跨域加載腳本,且腳本無序執(zhí)行披诗。
5. 小結(jié)
異步加載腳本還普遍存在另一個問題:無法保持多個腳本的執(zhí)行順序(除了defer)撬即。
為了腳本依序執(zhí)行,可以采用如下方法:
1)定時器
利用setTimeout
或setInterval
監(jiān)控第一個腳本執(zhí)行情況呈队,一旦發(fā)現(xiàn)被執(zhí)行完搞莺,再繼續(xù)執(zhí)行下一個腳本。
- Script Onload
利用script元素的onload
和onreadystatechange
事件處理程序掂咒,例子如下:
<script>
var scrElem = document.createElement('script');
scrElem.src = 'a.js';
scrElem.onloadDone = false;
scrElem.onload = function () {
scrElem.onloadDone = true;
// 執(zhí)行第二個腳本
};
//針對IE瀏覽器
scrElem.onreadystatechange = function () {
if ((scrElem.readyState === 'loaded' || scrElem.readyState === 'complete') && !scrElem.onloadDone) {
scrElem.onloadDone = true;
//執(zhí)行第二個腳本
}
};
document.getElementsByTagName('head')[0].appendChild(scrElem);
</script>
注意:script.onload/onreadystatechange事件同樣會阻塞window.onload