
雖然這對(duì)頁面原有JS的執(zhí)行不會(huì)有大的影響,但會(huì)影響到第三方JS代碼本身的下載與執(zhí)行。如何解決這個(gè)問題呢?
你可能已經(jīng)發(fā)現(xiàn)上面的例子有個(gè)問題:HTML代碼中 g.js 的位置在 test.js 之后卻先下載了。其實(shí)這得益于瀏覽器的預(yù)解析機(jī)制,會(huì)先對(duì)HTML代碼做靜態(tài)分析找到外鏈的JS和CSS文件,然后并行下載下來(但是執(zhí)行順序不變)。IE>=8 及其他主流瀏覽器基本都實(shí)現(xiàn)了這個(gè)功能。所以在這些支持預(yù)先載的瀏覽器中流程圖應(yīng)該是這樣的:

為了利用預(yù)加載這個(gè)特性,我們可以使用如下的寫法:
使用標(biāo)準(zhǔn)的 script 標(biāo)簽寫法,確保瀏覽器能夠正確的識(shí)別這是一個(gè)外鏈JS文件。同時(shí)設(shè)置 async 標(biāo)簽,瀏覽器便會(huì)異步加載 test.js 文件,不會(huì)暫停掉瀏覽器的解析執(zhí)行。流程圖如下:

這里有一個(gè) DEMO 。
但它也并不完美,因?yàn)橐恍?舊瀏覽器 并不支持 async 屬性。這會(huì)導(dǎo)致這個(gè) test.js 文件在這些瀏覽器中不是異步的,并且會(huì)阻止掉頁面渲染。有一個(gè)好消息是移動(dòng)瀏覽器大多都支持 async 標(biāo)簽,如果你的用戶大都是移動(dòng)瀏覽器的,或者你的產(chǎn)品不支持舊瀏覽器,那么你可以使用這種方法。
當(dāng)然如果你不介意第三方JS代碼(本身也支持支持)被延后到頁面解析完畢后執(zhí)行,那么你可以再加上 defer 屬性:
Firefox支持 defer 屬性要比支持 async 早一點(diǎn)點(diǎn)。而且當(dāng)瀏覽器同時(shí)使用了 async 和 defer 屬性之后, 瀏覽器會(huì)忽略 defer 屬性 。所以可以放心的同時(shí)使用 async 和 defer 屬性。對(duì)于不支持 async 的瀏覽器,會(huì)自動(dòng)fallback到 defer 。不過支持程度也就多了一點(diǎn)點(diǎn),F(xiàn)irefox的舊版占比已經(jīng)很低了,基本可以忽略不計(jì)。
頁面 alt="物聯(lián)網(wǎng)" width="550" height="296" />
可以看到因?yàn)?test.js 的下載速度變慢,整個(gè)頁面一直處于loading狀態(tài)。頁面的 load 事件要等到全部加載完成之后才會(huì)觸發(fā)。如果頁面中的主要邏輯是在頁面 load之后再執(zhí)行,那么頁面很可能會(huì)在很長(zhǎng)一段時(shí)間內(nèi)不可用。極大的影響了用戶的使用體驗(yàn)。
Friendly IFrame方法
為了解決這個(gè)問題,meebo的工程師想了一個(gè)方案來解決這個(gè)問題:
(function(url){
// 第一部分
var dom,doc,where,iframe = document.createElement('iframe');
iframe.src = "javascript:false";
iframe.title = ""; iframe.role="presentation";
(iframe.frameElement || iframe).style.cssText = "width: 0; height: 0; border: 0";
where = document.getElementsByTagName('script');
where = where[where.length - 1];
where.parentNode.insertBefore(iframe, where);
// 第二部分
try {
doc = iframe.contentWindow.document;
} catch(e) {
// IE下如果主頁面修改過document.domain,那么訪問用js創(chuàng)建的匿名iframe會(huì)發(fā)生跨域問題,必須通過js偽協(xié)議修改iframe內(nèi)部的domain
dom = document.domain;
iframe.src="javascript:var d=document.open();d.domain='"+dom+"';void(0);";
doc = iframe.contentWindow.document;
}
doc.open()._l = function() {
var js = this.createElement("script");
if(dom) this.domain = dom;
js.id = "js-iframe-async";
js.src = http://www.netofthings.cn/JieJueFangAn/2016-10/url;
this.body.appendChild(js);
};
doc.write('');
doc.close();
})('test.js');
上述代碼分為兩個(gè)部分:
創(chuàng)建了一個(gè)隱藏的 iframe 標(biāo)簽,設(shè)置其 src 值為JS代碼,然后插入到主頁面中
在 iframe 標(biāo)簽load之后加載JS腳本
這樣加載Javascript,就不會(huì)阻止瀏覽器的>onload 事件比較動(dòng)態(tài)創(chuàng)建 script
標(biāo)簽dynamic_script.html是否是(IE<=9除外)兼容性最好、普適性最高的方案