javascript腳本加載 js load block
心細的用戶,可以從上面的http瀑布圖比較看出:只有等到a.js加載完了,纔開始加載b.js,然後再加載圖片資源。我們稱a.js阻塞b.js。這種現象,稱之爲js load block。
//filename:index.html
<html>
<head>
<script type="text/javascript" src="a.js"></script>
<script type="text/javascript" src="b.js"></script>
</head>
<body>
<img src="http://static.perfgeeks.com/wp-content/uploads/2011/01/p_460_001.jpg" />
</body>
</html>
有沒有辦法,讓這三個不同的資源a.js, b.js, p_460_001.jpg同時加載,減少頁面加載時間,避免因阻塞導致的減速影響。通常有以下幾個辦法
把所有javascript都內嵌在頁面中- XHR Eval
- XHR Injection
- Iframe
- Script DOM Element
- Defer
- document.write
這裏着重介紹最常用的XHR Injection和Script DOM Element二種方法
XHR Injection
XHR Injection和XHR Eval注入技巧都是通過XMLHttpRequest來獲取Javascript腳本資源。然後,XHR Eval通過javascript函數eval執行腳本。而XHR Injection則是通過創建一個script的DOM元素,然後把XMLHttpRequest的響應注入script中。某些時候,eval會比較慢,所以我們不推薦使用XHR Eval技巧。
<html>
<head>
<script type="text/javascript">
function load_js(src) {
var xml_http = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
xml_http.onreadystatechange = function() {
if (xml_http.readyState == 4) {
var script_element = document.createElement('script');
document.getElementsByTagName('head')[0].appendChild(script_element);
script_element.text = xml_http.responseText;
}
}
xml_http.open('GET', src, true);
xml_http.send('');
}
load_js('a.js');
load_js('b.js');
</script>
</head>
<body>
<img src="http://static.perfgeeks.com/wp-content/uploads/2011/01/p_460_001.jpg" />
</body>
</html>
上圖可以看到,a.js, b.js和圖片資源並行加載。但是XHR Injection有一個限制,不支持跨域加載。即獲取的腳本必須部署在與頁面相同的域中。同時,該技術不能保證腳本的執行順序。
Script DOM Element
相較於XHR Injection而言,Script DOM Element技巧支持跨域加載,即可以加載來自不同域的text/javascript資源文件。這是因爲該技術利用javascript動態創建script DOM元素並且設置src。
<html>
<head>
<script type="text/javascript">
function load_js(src) {
var script_elem = document.createElement('script');
script_elem.type = 'text/javascript';
script_elem.src = src;
document.getElementsByTagName('head')[0].appendChild(script_elem);
}
load_js('a.js');
load_js('b.js');
load_js('http://www.jt-tech.net/misc/jquery.js');
</script>
</head>
<body>
<img src="http://static.perfgeeks.com/wp-content/uploads/2011/01/p_460_001.jpg" />
</body>
</html>
上圖也解決了javascript加載阻塞產生的減速影響,即a.js、b.js還有跨域jquery.js等並行加載。這種技巧也有一個小小缺陷,就是在某些瀏覽器(Firefox2.0/3.0/3.1、Safari3.2.1/4.0、Chrome1.0)會阻塞onload事件,影響用戶體驗
阻塞渲染、阻塞onload、執行順序
阻塞渲染,當使用script src技術加載腳本時,瀏覽器停止渲染所有腳本後面的內容。這種阻塞給用戶帶來十分差勁的用戶體驗。應該儘量地避免這種情況出現。
//filename:cat bd_i.html
<html>
<head>
<script type="text/javascript" src="c.php">
</script>
</head>
<body>
hello world
</body>
</html>
//filename:vbd_i.html
<html>
<head>
<script type="text/javascript">
function load_js(src) {
var xml_http = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
xml_http.onreadystatechange = function() {
if (xml_http.readyState == 4) {
var script_element = document.createElement('script');
document.getElementsByTagName('head')[0].appendChild(script_element);
script_element.text = xml_http.responseText;
script_element.type = 'text/javascript';
}
}
xml_http.open('GET', src, true);
xml_http.send('');
}
load_js('c.php');
</script>
</head>
<body>
hello world
</body>
</html>
//filename: c.php
<?php
sleep(2);
?>
function c() {
return 2;
}
二幅圖對比,我們可以看到那條豎着的藍線,表示腳本渲染完成。使用script src技術,則等到c.php(text/javascrip)加載完成之後完成的渲染。而使用XHR Injection技術加載腳本,並沒有阻塞渲染。
阻塞onload,通常頁面的onload要直到所有資源加載完成時纔會被觸發。如果加載一個較大的javascript文件,用戶需要等待更長時間,瀏覽器狀態欄纔會顯示“完成”,同時還有可能延遲默認輸入框獲取焦點。導致較差的用戶體驗。
執行順序,當一個頁面包含多個text/javascript腳本文件的時候。這些高級技術,使得這些腳本同時(並行)加載,受網絡與文件大小的影響,腳本文件到到達順序與開發人員期望的順序有可能不一致。所以,使用這些高級技術的時候,儘量避免將相互調用的函數分散在不同的文件裏面。