正文
學習JavaScript就不得不提到Ajax,從2005年開始,Ajax技術就開始席卷整個Web世界。作為一個前端來說,大部分時間中都是使用的庫中封裝好的ajax模塊(jQuery),即使已使用到滾瓜爛熟,也不一定就能說明白,Ajax到底是什么,它是怎么實現的,所以這幾章作為開年來的復習篇章,將之前的閱讀筆記進行整合。相關的轉載皆在文章中做了說明。
XMLHttpRequest對象
將Ajax具象化,可以看出其就是無需刷新頁面而可以從服務器端取得數據的一種技術,所以微軟率先引入了XMLHttpRequest。在IE7+以及現代瀏覽器中都可以直接獲取該對象。
從中我們可以看到,自readyState
上面都是靜態函數。而readyState表示了請求的狀態:
- value=0,open()方法還未被調用
- value=1,send()方法還未被調用
- value=2,send()方法已經被調用,響應頭和響應狀態已經返回
- value=3,響應體下載中,responseText中已經獲取了部分數據
- value=4,請求完成,整個請求過程已經完畢。
其它的屬性可以從MDN中了解。
open()和send()
作為客戶端來說,首先需要建立請求:
xhr.open("get","example.json",false,"","");
// example.json的內容
// {xhr:"11"}
特意將所有參數都補全了,open()
方法中一共有五個參數,分別是
- method:請求所使用的HTTP方法,例如"GET","POST"
- url:該請求索要訪問的URL
- async:一個可選的布爾值參數,默認為true,意味着是否執行異步操作,如果值為false,則send()方法不會返回任何東西,直到接受到了服務器的返回數據
- user:用戶名,可選參數,默認參數為空
- password:密碼,可選參數,默認參數為空
可對照上面介紹的readyState
,會發現其值已變為1了。
然后發送請求:
xhr.send("test");
這里的send()
中,唯一的參數代表着作為請求主體發送的數據,如果不需要通過請求主體發送數據,則必須傳入null。
現在我們就得到了第一次請求的結果了,
可以看到XMLHttpRequest
報錯了,一共兩條,對應open()
和send()
,因為open中的url所填寫的是本地文件的路徑,在chrome中並不支持通過xhr對象直接訪問本地資源,而在火狐中,可以得到正確的請求完成結果:
因為火狐瀏覽器支持直接訪問本地資源,所以接下來的操作可以在火狐中或者配置一個本地的服務器環境就可以看到send()
之后的結果了。當然,這里的報錯請求也是需要做處理的,但先來把整個流程跑完,再開始這一部分的修改。
當修改之后,我們可以看到錯誤信息沒有了,在成功之后,xhr的readyState
變為了4,同時,我們可以看到,xhr中的其他屬性值也發生了變化,response
和responseText
的值正好是我們放在example.json中的數據。記下屬性簡介:
- responseText:作為響應主體被返回的文本
- responseXML:如果響應的內容類型是"text/xml"或"application/xml",這個屬性將保存包含響應數據的XML DOM文檔
- status:響應的HTTP狀態
- statusText:HTTP狀態的說明
因為當XMLHttpRequest
拋出異常時(比如說上面的請求本地文件),readyState
仍然會為4,所以這時候,檢查status就很有必要了。
var xhr = new XMLHttpRequest(),el = document.getElementById("write");
try{
xhr.open("get","example.json",true);
xhr.send(null);
}catch(err){
console.log(err);
}
el.innerHTML= "當前HTTP狀態為:"+xhr.status+";內容為"+xhr.responseText;
if( (xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 ){
alert("請求成功");
}else{
alert("請求失敗");
}
status
就是HTTP狀態碼,通常1字頭代表着消息,2字頭代表着成功,3字頭代表重定向中,4字頭代表請求錯誤,5(6)字頭代表服務器錯誤。像404和503就是經常出錯的代表字符。
而readyState
有onreadystatechange()
方法,來監聽它的變化,所以上述可改為
xhr.onreadystatechange = function() {
if( (xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 ){
alert("請求成功");
}else{
alert("請求失敗");
}
};
try{
xhr.open("get","example.json",true);
xhr.send(null);
}catch(err){
console.log(err);
}
在open之前指定onreadystatechange
可以確保跨瀏覽器兼容性。
另外,在接收到響應之前還可以調用abort()方法來取消異步請求。
try{
xhr.open("get","example.json",true);
xhr.abort();
xhr.send(null);
}catch(err){
console.log(err);
}
HTTP頭部信息
每個HTTP請求和響應都會帶有相應的頭部信息,默認情況下,在發送XHR請求的同時,還會發送下列頭部信息:
- Accept:瀏覽器能夠處理的內容類型
- Accept-Charset:瀏覽器能夠顯示的字符集
- Accept-Encoding:瀏覽器能夠處理的壓縮編碼
- Accept-Language:瀏覽器當前設置的語言
- Connetcion:瀏覽器與服務器之間連接的類型
- Cookie:當前頁面設置的任何Cookie
- Host:發出請求的頁面所在的域
- Referer:發出請求的頁面的URL
- User-Agent:瀏覽器的用戶代理字符串
使用setRequestHeader()
可以設置自定義的請求頭部信息。這個方法接受兩個參數:頭部字段的名稱和頭部字段的值:
xhr.open("get","example.json",true);
xhr.setRequestHeader("Myheader","myvalue");
xhr.send(null);
當然,如果調用XHR對象的getResponseHeader(key)
之后,就能取得對應key的響應頭部信息,而getAllResponseHeaders()
方法則可以取得一個包含所有頭部信息的長字符串。
request header是請求頭部,而response header是響應頭部,xhr中沒有getRequestHeader()方法,也沒有setResponseHeader方法,說明了請求和響應的分離與統一,對於詳細的頭部信息說明,有興趣的可以看下w3c提供的Header Field Definitions
GET,POST
這是我們日常開發所最高頻次使用的請求類型,兩者都是與服務器端做通信時,用於查詢信息或存儲信息,實質與定義有關,與使用無關;首先是GET:
xhr.open("get","example.json?value=1",true);
這里可以看出,get方式實質上是接在url?之后的字符串,當然,最好進行編碼encodeURLComponent()
,以確保查詢字符串格式良好,可以有效預防亂碼問題。
然后是POST:
xhr.open("post","example.json",true);
xhr.send("{key:0,key1=1}");
可以看到,兩者的傳遞信息的方式並不相同,相比較而言,使用post方式可以不限格式的提交數據,而get受限於url的格式問題。
url長度理論上沒有長度限制,但因為各種瀏覽器所支持的url長度不同,特別是老版本的ie,限制是2083字節,而post因為走的header部分,且在form時,可以與表單數據一起提交,所以數據量取決於服務器的處理能力。至於安全問題么,只能說在現在這個時代里,客戶端所發出來的東西都需要經過層層校驗來確認安全性,而get和post與之沒有太多的關系。
模擬form表單提交:
xhr.open("post","example.json",true);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xhr.send("key=0&key1=1}");
XMLHttpRequest 2級
FormData
作為表單而言,序列化是使用很頻繁的,jQuery里也有相應的函數實現(serialize()),而如果使用FormData:
xhr.open("post","example.json",true);
var form = document.getElementById("user-info");
xhr.send(new FormData(form));
就可以不必明確的在XHR對象上設置請求頭部。
超時設定
timeout表示請求在等待響應多少毫秒之后就會終止。在給timeout設置一個數值后,如果在規定的時間內瀏覽器還沒有收到響應,那么就會觸發timeout事件,進而會調用ontimeout()
回調。
overrideMimeType()
該方法用於重寫XHR響應的MIME類型。在重寫服務器返回的MIME類型上比較有用,可以指定返回的MIME類型進行處理,需要放在send()
之前。
跨域
通常情況下,如果直接訪問與包含XHR對象的頁面不同域的資源時,是無法訪問的,但是,有時又需要與外部域的資源進行交互,所以,CORS(Cross-Origin Resource Sharing,跨域資源共享)
就可以解決這個問題。其就是使用自定義的HTTP頭部讓瀏覽器與服務器進行溝通,從而決定請求或響應是否成功。更詳細的內容可以去看下大神的博客跨域資源共享 CORS 詳解(阮一峰)
因為整個CORS過程其實是瀏覽器能夠自動的對於跨域的請求附加頭信息,所以,只要服務器實現了CORS接口,那么,就已經可以進行跨域。然后主要看下從客戶端可以直接請求跨域的資源的方法
圖像Ping
作為<img>
來說,平常自然會與它頻繁的打交道,而<img>
可以從任何地址中獲取圖像信息,所以,可以通過這種方式,再加上Image對象的onload()
和onerror()
方法,單向的向服務器發送請求:
var img = new Image();
img.onload = img.onerror = function(){
console.debug("ok");
}
img.src = "http://url?params=1";
這樣就可以發送簡單的GET請求,但問題也很明顯,拿不到服務器返回的響應文本,而且只能發送GET請求。
JSONP
作為使用頻次非常高的一種方式,其一般的表現形式都是:
callback({"name":"test"})
由回調函數和數據組成,其中的數據就是callback()
的參數,那么就可以知道,如果我們將請求當成<script>
標簽的url時,就可以在請求完成之后,直接運行這個回調函數了,首先來個例子,單純的通過<script>
獲取數據。
function jsonp1(response){
console.debug(response);
}
var script = document.createElement("script");
script.src = "http://url?callback=jsonp1";
document.body.insertBefore(script,document.body.firstChild);
當載入頁面時,因為插入之后立即執行了jsonp1()
,所以就可以拿到response的值了,也就是服務器返回的數據。但是要確認jsonp請求是否失敗並不容易,當歸納到zepto和jQuery時再詳細的介紹這一方法。
小結
本文歸納了之前在工作和學習中一小部分的筆記,並且通過閱讀書籍《JavaScript高級程序設計》,將AJAX初步介紹了下,之后的zepto和jQuery的AJAX部分才算是實際應用部分,更新只能在有空的時間了,開年確實時間寶貴啊。