日常開發中一般都會使得ajax去獲了數據,但有兩點是需要值得注意的:
1、ajax請求隊列
2、ajax的超時處理
爲什麼要注意這兩點?爲了讓用戶在其可視區域內更快速的看見內容。
假設頁面結構分爲三欄:左、中、右,而且頁面數據會比較多,頁面呈現的順序則是是按從上而下執行的(當然是從左至右開始,一個模塊一個模塊加載數據),如果不採用隊列,那麼在頁面可視範圍之外的模塊可能已經加載完數據了,而可視範圍之內(假設爲第一屏)的模塊卻尚未開始接收數據,這一類應用如:搜狐博客、新浪博客、網易博客等…
既然是採用了隊列,那麼又會有一個新的問題:需要保證一個請求的時候不能太長,不能因爲一個請求而導致後續的請求被阻塞了。在這兩點上jQuery做的其實都挺不錯的。隊列的處理上,已經有一個插件了,叫ajaxManager,例子和鏈接在這裏:http://www.protofunc.com/scripts/jquery/ajaxManager/;而在超時的處理上,jquery本身就支持傳遞參數timeout來進行設置(默認是沒有設置的)。它沒有考慮IE8,儘管IE已經支持xhr對象的timeout屬性。
從ajax創建開始,這裏優化的一點是針對IE瀏覽器,只循環獲取一次使用哪種MSXML庫,副作用就是需要使用額外的屬性來記錄它
function createXHR() { if (typeof XMLHttpRequest != 'undefined') { return new XMLHttpRequest(); } else if (typeof ActiveXObject != 'undefined') { if (typeof arguments.callee.activeXString != 'string') { var version = ["MSXML2.XMLHTTP.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"]; for (var i = 0, len = version.length; i < len; ++i) { try { var xhr = new ActiveXObject(versions[i]); arguments.callee.activeXString = versions[i]; return xhr; } catch (ex){ } } } return new ActiveXObject(arguments.callee.activeXString); } else { throw new Error("No XHR object available."); } }
創建的xhr對象,它對應有5狀態(readyState屬性)
0 Uninitialized(尚未調用open方法)
1 Loading (已調用open,尚未調用send)
2 Loaded (已經調用send,尚未接收到響應)
3 Interactive (開始接收數據)
4 Complete (數據接收完畢,響應內容解析完成)
在判定一個請求是否已經完成的時候,驗證xhr的status有一點是需要注意的:“有的瀏覽器會錯誤地返回204狀態碼”,而IE(非原生的XHR對象)中會將204設置爲1223,Opera會在取得204時將status設置爲0,而Safari 3之前的版本會將status設置爲undefined
最終驗證請求是否成功的代碼將會是:
( xhr.status >= 200 && xhr.status < 300 ) || xhr.status === 304 || xhr.status === 1223 || xhr.status === 0
另外在send的時候,還需要注意的是如果不需要通過請求主體發送數據,最好是傳入參數,因爲send方法的參數
對於有些瀏覽器是必需的,建議一般傳null即可
在發送請求時,可以通過setRequestHeader來設置HTTP頭部信息,在使用GET請求時,可以在頭部加上If-Modified-Since、Cache-Control參數來達到刷新緩存數據的目的(如果採用在URL上加隨機數據或是時間戳,資源並沒有被緩存)
xhr.setRequestHeader('If-Modified-Since', 'Thu, 1 Jan 1970 00:00:00 GMT'); xhr.setRequestHeader('Cache-Control', 'no-cache');
在響應完成後,可以使用getResponseHeader、getAllResponseHeaders兩個方法來獲取指定或是全部的響應頭的HTTP信息
剩下的一個問題是,處理ajax超時的問題。jquery中的做法是使用定時器來檢測xhr的狀態,而使用延時器來解決超時的問題:
setInterval(onreadystatechange, 13);
setTimeout(fn, timeout);
而在自定義的onreadystatechange函數中會檢測傳入的參數,如果參數爲“timeout”則說明超時了,先調用xhr的abort取消請求,然後再調用complete方法。至於間隔時間爲什麼是13,這個沒仔細去研究它
正常情況下,如果readyState爲4,則先清除定時器,然後再檢測響應的數據。而setTimeout中的fn函數,在處理時會先檢測請求是否已經處理過了,這裏它並沒有對延時器進行引用,會導致的一種情況是,請求已經結束,延時器還在跑,直到達到指定的時間間隔。
最後如果ajax請求爲異步的話,別忘記將xhr置爲null==>xhr = null; 以防止內存泄漏的問題
IE8中直接寫xhr.timeout = xxx;然後當超時時,會調用xhr的ontimeout方法,不過需要注意的問題是,當調用ontimeout事件時,此時的readyState可能已經變爲了4,此時如果去訪問status則會導致錯誤(最好使用try{}catch{}進行捕獲一下)
到目前爲止,除IE外,其它瀏覽器支持xhr對象的onload事件,只要瀏覽器開始接收到響應,就會觸發它,所以在這個函數裏面還是需要對它的status屬性進行判斷。
最後一點是在FF 1.5之後,它支持progress事件,這意味着可以顯示當前請求的進度(不再是枯燥的loading了)。
在onprogress事件中會傳入一個event對象,它的target是對應的xhr對象,它包含了兩個額外的屬性:position、totalSize。
其中position表示已接收的字節數,totalSize表示根據Content-Length響應頭部確定的預期字節數。
var xhr = new createXHR(); xhr.onload = function() { //... } xhr.onprogress = function(evt) { var percent = (evt.position / evt.totalSize)*100; } xhr.open("get", url, true); xhr.send(null);