預先載入的好處可以讓網頁更快的呈現給使用者,缺點就是可能會增加無用的請求(但圖片、css、js這些靜態檔案可以被緩衝),如果使用者訪問的頁面裡面的css、js、圖片被預先載入了,使用者開啟頁面的速度會快很多,提升使用者體驗。在用到一些大圖片展示的時候,預先載入大圖是很不錯的方法,圖片更快的被呈現給使用者。不多說了,作為一個前端攻城師都懂的,下面分享我做的測試和得到的結果。
先說需要知道的伺服器返回的status code:
status-code: 200 - 用戶端請求成功
status-code: 304 - 檔案已經在瀏覽器緩衝中,伺服器告訴用戶端,原來緩衝的文檔還可以繼續使用。
本文測試判斷檔案被是否被緩衝,用的就是判斷是否返回304.
下面針對預先載入的幾個方法,在不同的瀏覽器下載入img/js/css做個測試,主要包括new Image()、object、iframe。以下載入測試的js、css、圖片檔案,是從幾個門戶網站找的(為啥找幾個?是為了儘可能滴測試到特殊的情況,測試中還真遇到了)。
1、測試用new Image()預先載入
1.1、new Image()載入
複製代碼 代碼如下:
new Image().src = 'yun_qi_img/T1iQhUXnxpXXXXXXXX-171-48.png'; //淘寶
new Image().src = 'yun_qi_img/logo_2011_02_22.png'; //拍拍
new Image().src = 'yun_qi_img/logo.png'; //有啊
new Image().src = 'yun_qi_img/logoNew_nocache.png'; //新浪*/
然後再把圖片添加到頁面內:<img src="xxx" />
載入圖片沒啥好說的,IE6-9/CM/FF/OP/都返回304,預先載入成功。
1.2、測試用new Image()載入css
複製代碼 代碼如下:
new Image().src = 'http://a.tbcdn.cn/p/global/1.0/global-min.css'; //淘寶(1)
new Image().src = 'http://static.paipaiimg.com/member/activate.css'; //拍拍(2)
new Image().src = 'http://co.youa.baidu.com/picture/services/base.css'; //有啊(3)
new Image().src = 'http://img1.t.sinajs.cn/t35/skin/skin_008/skin.css'; //新浪(4)
// http://auto.sina.com.cn/css/newstyles.css
// 可以用這個測試IE下Expires設定的時間小於目前時間的情況
再把css添加到頁面內
這個有區別了:
CM/OP,都返回304(無論有沒有設定Expires)。
FF, 全部返回了200。
IE,1/2/4都返回304,而3返回200。對比返回的HTTP-Header可以發現:1/2/4都設定了Expires到期時間,而3沒有設定。
說明IE下緩衝需要設定Expires(並且設定的時間要大於目前時間),而FF不支援利用new Image()預先載入。
1.3、測試用new Image()載入js
複製代碼 代碼如下:
new Image().src = 'http://a.tbcdn.cn/s/kissy/1.1.6/kissy-min.js'; //淘寶(1)
new Image().src = 'http://static.paipaiimg.com/js/pp.noticeBoard.js'; //拍拍(2)
new Image().src = 'http://co.youa.baidu.com/picture/services/cms_core.js'; //有啊(3)
new Image().src = 'http://js.t.sinajs.cn/t35/miniblog/static/js/top.js'; //新浪(4)
new Image().src = 'http://shop.qq.com/act/static/week/fri/bang/day_1_p_0_10.js'; //QQ(5)
再把js添加到頁面內。
CM/OP,都返回304
FF,只有5返回了304,也只有5的HTTP-Header最簡單(包括:Date、Server、Expires、Cache-Control)。
另外幾個的回應標頭資訊內容都比較多,但也都設定了5裡面的這幾個。找規律,發現另外幾個的回應標頭都有:Content-Type:text/javascript,而5裡面沒這個。
IE,2/5返回了304,1/3/4返回200,對比回應標頭,應該還是Content-Type影響的,IE裡面2/5都沒看到Content-Type。
另外,感謝AndrewZhang(http://www.cnblogs.com/AndyWithPassion/)提到,IE下 image 預先載入js在httpwatch下查看content,資源的載入並不是完整的。我這裡測試也是如此。貌似有位元組限制,測試中2返回的是完整的,5有一部分內容丟失了。所以用new Image載入JS一點都不可取。
這裡總結下:預先載入圖片用new Image()相容性沒問題。但是css/js只有OP/CM可以,IE/FF基本是無效(這點IE/FF到挺有默契)。
2、測試用object預先載入
var doc = document,
obj = doc.createElement('object');
//obj.data = '123.js'; //Ps: 這樣寫OP下無效(會把data的內容作為object標籤裡的text node)
//obj.setAttribute('data', '123.js'); // img、css、js
obj.style.cssText = 'position:absolute;top:-1px;width:1px;height:1px;';
// obj.style.width = obj.style.height = 0;
doc.body.appendChild(obj); // 插入object 標籤需要插入到非head部分,以觸發載入*/
//obj.onload = function(){ alert('loaded') }; // FF/OP/Webkit支援(如果data是圖片,IE9也可以)
然後再吧object裡面data載入的檔案,建立標籤加到HTML內測試。
測試結果:
FF/OP/CM: 無論是img/js/css,都返回304。
IE6-8:用object載入img/js/css,會直接Aborted。
IE9比較特殊:
IE9載入js/css,先請求並返回HTTP200,再請求並Aborted,這裡實際上是請求1次(第2次Aborted了)。
IE9載入img的情況,先請求並返回HTTP200,再請求返回圖片,所以圖片需要請求2次。
IE9的第1次請求返回的內容是空的(並且此時瀏覽器一般會卡住,或者直接失去響應)。 IE9首先會請求url,擷取檔案類型,判斷是JS/CSS就Aborted,判斷是圖片才載入。
至於IE9第1次請求,大概是靠讀取HTTP頭資訊來得到檔案類型,或者偷偷把檔案下載下來,然後在沙箱裡面測試檔案類型。
一個有意思的事情,比如用object載入JS,IE9有時也能載入進來,也就是第1個請求沒判斷出檔案是JS(想看到這個要看運氣了,貌似網速慢的時候可能發生)
據說以前IE是靠檔案尾碼來判斷檔案類型的,後期用HTTP頭資訊來判斷,而他們都可以偽造,所以object在IE下存在安全問題。
IE6/7,如果檔案尾碼尾碼為.js/.css不會發出請求,如果改成http://xxx/test.js?123.png,就發送請求了,然後用script標籤引入,發現可被緩衝(css這樣搞也OK^^)。
IE8,尾碼為js/css也不會發出請求,改尾碼為png可以發出請求並得到內容,然後頁面建立標籤引入,檔案並沒有被緩衝。但如果檔案是真正的圖片就被緩衝了。
題外話:通過上面可以發現,隨著IE的升級,安全性也越來越高了。
So,這裡的結論是:FF/OP/CM下可以用object預先載入,IE就千萬別用了。
3、測試用iframe預先載入
先建立頁面a.html,然後加上下面的js。
複製代碼 代碼如下:
var doc = document,
ifm = doc.createElement("iframe");
//ifm.id="preLoadIfm";
// ifm.style.border = ifm.width = ifm.height = 0;
ifm.style.cssText = 'position:absolute;top:-10px;border:0;width:1px;height:1px;';
ifm.scrolling = "no";
doc.body.appendChild(ifm);
window.onload = function(){ // 預先載入當然最好是window.onload之後觸發
//要觸發onload,需要先appendChild,然後再寫onload(如果順序顛倒,IE下不能觸發)
// ifm.onload = function(){ alert('ifm loaded'); }
// contentWindow.document-所有都支援,contentDocument-IE9/FF/OP/CM支援
var ifmDoc = ifm.contentDocument || ifm.contentWindow.document;
ifmDoc.open();
ifmDoc.write('<!doctype><html><head></head><body>');
//ifmDoc.write('<style>html{background:#000;color:#fff}</style>'); // 用於測試
//ifmDoc.write('<script>alert("a")<\/script>'); // 用於測試
//ifmDoc.write('<p>test</p><p>test</p><p>test</p><p>test</p><p>test</p>');// 用於測試
// 開始載入
ifmDoc.write('<link rel="stylesheet" href="http://localhost/123.css?2011" />');
ifmDoc.write('<script defer src="http://localhost/123.js?2011"><\/script>'); //不加defer,你會發現IE卡死。。
ifmDoc.write('<img width="1" height="1" src="http://localhost/123.png?2011" />');
ifmDoc.write('</body></html>');
ifmDoc.close();
};
然後建立新頁面b.html,把要上面預先載入的檔案加到html裡面,測試是否已經預先載入。
結果:IE/FF/OP/CM都成功預先載入。
需要說明的是:當開啟a.html後,再重新整理頁面後,iframe內負載檔案的情況。
FF,返回200(注意,這個200不是伺服器返回的200,是請求緩衝成功。因為發送請求的時間顯示的是0)。
CM,顯示狀態是(from cache).
OP,雖然顯示狀態是n/a,但是也是from cache。 IE,IE內建的調試工具顯示304,HttpWatch顯示from cache。
測試環境:
WIN7 EN SP1:OP 11.50、IE7-9、FF 3.6/6.0、Chrome 10
XP EN SP3:IE6
XP EN SP3:IE7
XP CN SP3:IE8
工具:IE9內建的調試工具、HttpWatch、firebug、chrome內建的調試工具、Opera Dragonfly。
最後得出的結論:js預先載入圖片使用new Image()基本夠用了。但是css、js特殊一些,使用object需要判斷瀏覽器。如果考慮到js、css、img都能相容實現預先載入,可以考慮使用iframe。
另外,上面的方法建立iframe後,不使用write()寫入要載入的檔案,直接設定iframe.src = "cache.html",然後把要預先載入的檔案寫在cache.html內也是可行的(以前看過有文章介紹新浪微博是這樣做的,但是文章地址找不到了,搜尋也沒搜到),cache的網址我收藏了:http://tjs.sjs.sinajs.cn/miniblog2/static/html/cache.html,但是看微博的首頁沒找到這個,不知道在哪個頁使用的。
其他預先載入的一點補充
doc.createElement('script') 可以預先載入js,如果js裡面有對頁面的操作,就會對頁面產生影響。
doc.createElement('link') 可以預先載入css,但是對當前頁面的樣式也可能會有影響。
所以這樣預先載入不太可取。
用ajax載入img/js/css,相容性不錯,檔案可以被緩衝,但是只能限制同域,所以使用範圍有限。
預先載入圖片還可以利用CSS的背景圖片實現。牛人lifesinger之前寫過關於圖片的HTTP請求的文章,不過他部落格以前的資料沒了。網上搜尋到一篇:http://www.jb51.net/web/110275.html。 文章裡面提到了用背景圖和隱藏的img標籤來預先載入,調理很清晰。也可以作為參考。
另外,模仿新浪的cache.html自己寫了個,如果喜歡把iframe作為獨立檔案使用的可以作為參考。
複製代碼 代碼如下:
<!DOCTYPE html><html><head><meta charset="utf-8"></head><body>
<script>
//usage: cache.html?v=123
var win = window,
doc = document,
head = doc.getElementsByTagName("head")[0],
getQuery = function(){
var ret = {},
sch = win.location.search,
arr,
tmp;
if (sch) {
sch = sch.substr(1);
arr = sch.split("&");
for(var i = 0, j = arr.length; i < j; i++) {
tmp = arr[i].split('=');
ret[tmp[0]] = tmp[1];
}
}
return ret;
},
version = getQuery().v || '';
win.onerror = function(){return true}; //屏蔽js錯誤提示
win.onload = function(){
var b = doc.createElement("script");
b.src = 'http://xx/1.js?v=' + version;
head.appendChild(b);
//...
};
doc.write('<link rel="stylesheet" href="http://xxx/3.css?version=' + version + '" \/>');
</script>
<img src="yun_qi_img/4.png" />
</body></html>