這幾天一直在忙項目上線的問題,碰到了好多問題,比如非同步載入、事件冒泡、瀏覽器緩衝。這篇文章主要是檔案的同步載入和非同步載入。
先說下我碰到的問題:項目要接入到支付寶城市服務中,但是安卓和ios設定支付寶的導覽列內容有差異。安卓和ios都是拿頁面的title設定導覽列的內容,但是安卓支援用js動態修改title,ios是在頁面初始化時會預設拿title作為導覽列內容,初始化後用JS動態修改無效。支付寶提供了設定支付寶導航的jsAPI,需要載入兩個檔案,我之前是在項目中直接載入這連個檔案,在調用它提供的方法,沒問題完美設定。但是後來我感覺這樣不好我不是每個輸出的商戶都需要設定,所以我用navigator.userAgent判斷是不是在支付寶用戶端開啟,在動態添加這兩個JS檔案,在這就出現問題了,你動態添加完JS後,馬上執行裡面的方法有時會出現錯誤,說明你還沒有載入成功時就執行了方法。
這裡就是我們經常說的同步載入與非同步載入:
1. 同步載入
我們平時最常使用的就是這種同步載入形式:
<script src="js/jQuery.js"></script>
同步模式,又稱阻塞模式,會阻止瀏覽器的後續處理,停止了後續的解析,因此停止了後續的檔案載入(如映像)、渲染、代碼執行。
js 之所以要同步執行,是因為 js 中可能有輸出 document 內容、修改dom、重新導向等行為,所以預設同步執行才是安全的。
以前的一般建議是把<script>放在頁面末尾</body>之前,這樣儘可能減少這種阻塞行為,而先讓頁面展示出來。
簡單說:載入的網路 timeline 是瀑布模型,而非同步載入的 timeline 是並行存取模型。
2. 常見非同步載入(Script DOM Element)
原生方法
(function() {
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'js/jQuery.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
})();
jQuery方法
$("head").append(“jQuery.js”)
非同步載入又叫非阻塞,瀏覽器在下載執行 js 同時,還會繼續進行後續頁面的處理。
這種方法是在頁面中<script>標籤內,用 js 建立一個 script 元素並插入到 document 中。這樣就做到了非阻塞的下載 js 代碼。
3. onload 時的非同步載入
(function() {
function async_load(){
var s = document.createElement('script');
s.type = 'text/javascript';
s.async = true;
s.src = 'js/jQuery.js';
var x = document.getElementsByTagName('script')[0];
x.parentNode.insertBefore(s, x);
}
if (window.attachEvent)
window.attachEvent('onload', async_load);
else
window.addEventListener('load', async_load, false);
})();
這和前面的方式差不多,但關鍵是它不是立即開始非同步載入 js ,而是在 onload 時才開始非同步載入。這樣就解決了阻塞 onload 事件觸發的問題。
補充:DOMContentLoaded 與 OnLoad 事件
DOMContentLoaded : 頁面(document)已經解析完成,頁面中的dom元素已經可用。但是頁面中引用的圖片、subframe可能還沒有載入完。
OnLoad:頁面的所有資源都載入完畢(包括圖片)。瀏覽器的載入進度在這時才停止。
4. jQuery的非同步載入
jQuery.getScript(url, [callback])
參數:url:待載入 JS 檔案地址。
callback:成功載入後回呼函數。
$.getScript("test.js", function(){
alert("Script loaded and executed.");
});
這和前面的方法差不多載入 test.js 成功後,調用方法顯示資訊。
5、async 和 defer 屬性
1. defer 屬性
<script src="js/jQuery.js" defer></script>
defer屬性聲明這個指令碼中將不會有 document.write 或 dom 修改。
瀏覽器將會並行下載 file.js 和其它有 defer 屬性的script,而不會阻塞頁面後續處理。
defer屬性在IE 4.0中就實現了,超過13年了。Firefox 從 3.5 開始支援defer屬性 。
註:所有的defer 指令碼保證是按順序依次執行的。
2. async 屬性
<script src="js/jQuery.js" async></script>
async屬性是HTML5新增的。作用和defer類似,但是它將在下載後儘快執行,不能保證指令碼會按順序執行。它們將在onload 事件之前完成。
Firefox 3.6、Opera 10.5、IE 9 和 最新的Chrome 和 Safari 都支援 async 屬性。可以同時使用 async 和 defer,這樣IE 4之後的所有 IE 都支援非同步載入。
3. 詳細解釋
<script> 標籤在 HTML 4.01 與 HTML5 的區別:
· type 屬性在HTML 4中是必須的,在HTML5中是可選的。
· async 屬性是HTML5中新增的。
· 個別屬性(xml:space)在HTML5中不支援。
說明:
1. 沒有 async 屬性,script 將立即擷取(下載)並執行,然後才繼續後面的處理,這期間阻塞了瀏覽器的後續處理。
2. 如果有 async 屬性,那麼 script 將被非同步下載並執行,同時瀏覽器繼續後續的處理。
3. HTML4中就有了defer屬性,它提示瀏覽器這個 script 不會產生任何文件項目(沒有document.write),因此瀏覽器會繼續後續處理和渲染。
4. 如果沒有 async 屬性 但是有 defer 屬性,那麼script 將在頁面parse之後執行。
5. 如果同時設定了二者,那麼 defer 屬性主要是為了讓不支援 async 屬性的老瀏覽器按照原來的 defer 方式處理,而不是同步方式。