第一章 加載和執行
大多數瀏覽器是使用單進程來處理UI和javascript,因此無法並行下載,當瀏覽器讀取到javascript時會先下載javascript在進行頁面的渲染,javascript讀取的時間有多長,頁面停止渲染的時間就有多長。因爲在javascript中有可能會進行DOM操作。爲了保證瀏覽器能呈現出準確的界面,所以會先下載javascript。
1.腳本的位置
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript" src="f1.js"></script>
<script type="text/javascript" src="f2.js"></script>
<script type="text/javascript" src="f3.js"></script>
<link rel="stylesheet" href="style.css"/>
</head>
<body>
</body>
</html>
瀏覽器在遇到body標籤之前是不會進行頁面渲染的,因此瀏覽器會按照順序下載javascript。在一個大型網站中會加載很多腳本。如果先下載並javascript的話,那麼頁面會出現一片空白。下面是上述代碼的瀑布流圖
可以看出每個腳本都是在上個腳本下載並執行完後再開始下載的。
解決措施:將腳本儘量加載靠近標籤底部的位置。儘可能的減少對整個頁面下載的影響。
2.成組的腳本
每個http請求會產生額外的性能負擔。下載一個100KB的文件要比下載四個25KB的文件要快,所以我們要儘量將腳本合併。
解決措施:使用打包工具儘可能的將腳本合併,減少http請求,減少性能的負擔。
3.非阻塞腳本
保持較小體積的腳本,減少http請求只是創建反應迅速網站的第一步,功能越多的網站所需要的javascript代碼體積就會越大。儘管將所有代碼合併,只進行一次http請求,也會需要很長時間,在這期間,頁面會出現空白。因此可以將腳本逐步添加。
解決措施:
1.使用延遲腳本,即defer屬性,直到文檔載入並解析完成纔會執行
1.使用異步腳本,即async屬性,瀏覽器儘快執行腳本,但不阻塞文檔解析
defer:
優勢:帶有defer屬性的script會告訴瀏覽器,腳本中沒有影響dom的代碼,可以延遲執行。當腳本解析到script標籤時腳本會被下載。帶有defer屬性的腳本支持和其他資源並行下載。但是不會執行,直到dom節點加載完成後纔會執行。(onload事件之前)
缺點:因爲defer屬性只支持ie4或者firefox3.5以上因此並不是一個優秀的跨瀏覽器的解決方案,在不支持defer屬性的瀏覽器中script依舊會阻塞dom的加載。
async:
defer和async一樣,在下載的同時其他資源也可以並行下載。區別就在於,帶有async屬性的腳本,當下載完後會立刻執行。因此可能不會按照順序進行加載。如果腳本之間相互依賴可能會出現undifined或者報錯。
目前來看,最新版本的firefox和chrome(還有同樣webkit內核的safari),IE的話對於defer是一直都支持的,async屬性IE6~9都沒有支持(IE10毫無疑問的會支持),onload是在IE9中新加入的屬性.
4.動態添加腳本
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<input type="button" onclick="init()"/>
<script type="text/javascript">
var script=document.createElement('script');
script.type='text/javascript';
script.src='fn.js';
document.getElementsByTagName('head')[0].appendChild(script);
script.οnlοad=function(){
_fn();
};
_fn();//報錯
setTimeout(function(){
_fn()
},200)
</script>
</body>
</html>
當腳本進行下載的同時不會阻塞頁面其他資源的下載和代碼的執行,因此,如果加載的腳本中有需要下面代碼所調用的接口,則會出現錯誤。因此需要通過事件進行調用。
在firefox,chorme,opera都提供了onload事件供調用
在ie中提供了onreadychange事件,readystate伴隨着狀態一共有五種取值。
uninitialized:默認狀態
loading:開始下載
loaded:下載完成
interactive:下載完成且不可用
complete:所有數據已經準備好
function _loadscript(url,callback){
var script=document.createElement('script');
script.type='text/javascript';
script.src=url;
if(script.readyState){
script.onreadystatechange=function(){
if(script.readyState=='loaded'||script.readyState=='complete'){
script.onreadystatechange=null;
callback()
}
}
}
else{
script.οnlοad=function(){
callback();
}
}
document.getElementsByTagName('head')[0].appendChild(script);
}
5.XHR腳本注入
另一種無阻塞加載腳本的方式是通過XMLHttpRequest(XHR)對象獲取腳本並注入到頁面中:先創建一個XHR對象,然後用它下載JavaScript文件,然後通過創建動態script元素將代碼注入到頁面中。
var xhr = new XMLHttpResquest();
xhr.open("get","file.js",true);
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status>=200 && xhr.status<300 || xhr.status == 304){
var script = document.createElement("script");
script.type = "text/javascript";
script.text = xhr.responseText;
doument.body.appendChild(script);
}
}
}
xhr.send(null);
總結
將script標籤放在頁面底部,body閉合之前
合併腳本
使用無阻塞加載方式:defer,async屬性,動態創建script標籤,XHR對象注入JavaScript代碼