去百度運維部面試時,問的問題是:
1.當(dāng)CSS執(zhí)行卡住時秩仆,DOM會不會繼續(xù)渲染码泛?如果是JS卡住呢?
2.有沒有用過<script>
標簽的async
屬性澄耍?
該總結(jié)看似和以上兩個問題不相關(guān)噪珊,實則回答了以上問題。
一齐莲、為什么要JS異步加載痢站?
因為同步加載存在問題!
JS在默認情況下是以同步模式(又稱阻塞模式)加載的选酗,這里“加載”的意思是“解釋阵难、執(zhí)行”。在最新版本的瀏覽器中芒填,瀏覽器對于代碼請求的資源都是瀑布式的加載呜叫,而不是阻塞式的,但是JS的執(zhí)行總是阻塞的氢烘。這會引起什么問題呢怀偷?如果在頁面中加載一些JS,但其中某個請求遲遲得不到響應(yīng)播玖,位于此JS后面的JS將無法執(zhí)行椎工,同時頁面渲染也不能繼續(xù),用戶看到的就是白屏(此時JS在<head>
標簽之后引入)蜀踏。
<head>
<meta charset="UTF-8">
<title>test</title>
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js'></script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
</head>
<body>
<p>我是小仙女</p>
</body>
這是一個簡單的html文件维蒙,頁面的主體是簡單地文本段落,但是代碼執(zhí)行后遲遲都是空白果覆。為什么呢颅痊?因為第一個請求的JS遲遲無法加載,于是阻塞了后面代碼的執(zhí)行局待,頁面得不到渲染斑响。
那該怎么辦呢菱属?你可能會想到一個方法,把JS代碼放到</body>
標簽之前舰罚!好主意纽门!于是我們把JS放在HTML語句后面(這也是所提倡的頁面結(jié)構(gòu))。
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>我是小仙女</p>
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js'></script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
</body>
頁面瞬間被渲染营罢,問題似乎解決了赏陵,可是...
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>我是小仙女</p>
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js'></script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
alert("hello world");
</script>
</body>
我們在前面代碼的基礎(chǔ)上簡單加了一段代碼,但"hello world"遲遲無法被彈出饲漾,顯然是因為前面的JS請求阻塞了后面代碼的加載蝙搔。這時同步加載的問題依然存在:改變JS的加載位置只能改變頁面的渲染,JS還是會阻塞考传。
二吃型、如何實現(xiàn)JS異步加載?
異步加載即非阻塞加載僚楞,瀏覽器在加載JS的同時败玉,還會繼續(xù)進行后續(xù)頁面的處理。在本例中镜硕,我們想實現(xiàn)在請求第一段谷歌提供的JS的同時,下面的JS不受影響返干,繼續(xù)執(zhí)行兴枯。
如何實現(xiàn)?現(xiàn)在提供常用的幾種方法:
1.動態(tài)生成<script>
標簽
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>我是小仙女</p>
<script type="text/javascript">
(function(){
var s=document.createElement("script");
s.type="text/javascript";
s.src="http://china-addthis.googlecode.com/svn/trunk/addthis.js";
document.body.appendChild(s);
})();
</script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
alert("hello world");
</script>
</body>
但是上面的代碼還有點問題矩欠,這種加載方式在加載執(zhí)行完之前會阻止onload事件的觸發(fā)财剖,而現(xiàn)在很多頁面的代碼都在onload時執(zhí)行額外的渲染工作等,所以還是會阻塞部分頁面的初始化處理癌淮。
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>我是小仙女</p>
<script type="text/javascript">
(function(){
//function lazy(){
var s=document.createElement("script");
s.type="text/javascript";
s.src="http://china-addthis.googlecode.com/svn/trunk/addthis.js";
document.body.appendChild(s);
// }
// window.addEventListener("load",lazy,false);
})();
window.onload=function(){
alert("hello world");
};
</script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
</body>
上面帶注釋的代碼不能很好的渲染"hello world"躺坟,因為存在阻塞onload事件問題;可以把注釋去掉乳蓄,讓谷歌提供的JS在onload時才開始異步加載咪橙,這就解決了上述問題。
2.async
屬性
async
是HTML5的新屬性虚倒,該屬性規(guī)定一旦腳本可用美侦,則會異步執(zhí)行(一旦下載完畢就會立刻執(zhí)行)。
需要注意的是:async
屬性僅適用于外部腳本(只有在使用src屬性時)魂奥。
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>我是小仙女</p>
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js' async="async"></script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
alert("hello world");
</script>
</body>
3.defer
屬性
defer
屬性規(guī)定是否對腳本執(zhí)行進行延遲菠剩,直到頁面加載為止。
之前只有IE的hack支持defer
屬性耻煤,現(xiàn)在H5開始全面支持defer
屬性具壮。
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<p>我是小仙女</p>
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js' defer="defer"></script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
alert("hello world");
</script>
</body>
async
和defer
看起來差不多呀准颓?而且經(jīng)常一起出現(xiàn)!來辨析一下:
a.如果沒有async
和defer
屬性棺妓,那么瀏覽器會立即執(zhí)行當(dāng)前的JS腳本攘已,阻塞后面的腳本;
b.如果有async
屬性涧郊,加載和渲染后續(xù)文檔的過程和當(dāng)前JS的加載與執(zhí)行并行進行(異步)贯被,它是亂序執(zhí)行的,不管你聲明的順序如何妆艘,只要它加載完了就會執(zhí)行彤灶;
c.如果有defer
屬性,加載后續(xù)文檔元素的過程和JS的加載是并行進行(異步)的批旺,但是JS的執(zhí)行在所有元素解析完成之后進行幌陕,而且它是按照加載順序執(zhí)行腳本的
4.其他方式
a.XHR注入(通過XMLHttpRequest對象來獲取JS,然后創(chuàng)建一個script元素插入到DOM結(jié)構(gòu)中);
b.ajax eval(使用ajax得到腳本內(nèi)容汽煮,然后通過eval(xmlhttp.responseText)
來運行腳本);
c.iframe等