1. 起因
為什么我要寫這篇文章?原因是這樣的,在工作開發中,和后臺對接口的時候,遇到了一些請求上的問題。發現之前的同事封裝的api請求函數,是在superagent庫中的request上封了一層做請求函數的,對我要實現的需求不滿足。于是我便重新寫個滿足要求的請求函數。
2. 基本認識
ajax是asynchronous javascript and XML的簡寫,通過原生的XMLHttpRequest對象發出HTTP請求,得到服務器返回的數據后,再進行處理。通過與后臺通信包含下面幾個步驟:
- 創建ajax請求
- 發出http請求
- 收到服務器傳回的消息
- 更新網頁數據
2.1 創建ajax請求
ajax技術的核心是XMLHttpRequest對象(簡稱XHR),這是由微軟首先引入的一個特性,其他瀏覽器提供商后來都提供了相同的實現。XHR為向服務器發送請求和解析服務器響應提供了流暢的接口,能夠以異步方式從服務器取得更多信息,意味著用戶單擊后,可以不必刷新頁面也能取得新數據
IE5是第一款引入XHR對象的瀏覽器。在IE5中,XHR對象是通過MSXML庫中的一個ActiveX對象實現的,而IE7+及其他標準瀏覽器都支持原生的XHR對象
創建一個XHR對象,也叫實例化一個XHR對象,因為XMLHTTPRequest()是一個構造函數。下面是創建XHR對象的兼容寫法
var xhr;
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
}else{
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
注意
:如果要建立N個不同的請求,就要使用N個不同的XHR對象。當然可以重用已存在的XHR對象,但這會終止之前通過該對象掛起的任何請求。
2.2 發出http請求
在使用xhr的時候第一個調用的是open方法,如下,該方法接受3個參數,
xhr.open('GET','https://www.geekjc.com/fetch/library',false);
這個open方法有以下4點說明:
- open()方法的第一個參數用于指定發送請求的方式,這個字符串,不區分大小寫,但通常使用大寫字母。”GET”和”POST”是得到廣泛支持的.
‘GET’用于常規請求,它適用于URL完全指定的資源。當請求對服務器沒有任何副作用是可緩存的情況下,
‘POST’方法常用與HTML表單,他在請求主題中額外包含數據且這些數據常存在服務器上的數據庫中。相同的URL的重復POST請求從服務器的得到的相應可能不同
除了”GET”和”POST”之外,參數還可以是”HEAD”、”OPTIONS”、”PUT”。而由于安全風險的原因,”CONNECT”、”TRACE”、”TRACK”被禁止使用. - open()的第二個參數是URL,該URL對于執行代碼的當前頁面,且只能是同一域中使用相同協議和端口的URL發送請求。
- open()的第三個參數是表示是否異步發送請求的值,如果不填寫。默認為true,表示異步發送,
- 如果請求一個密碼保護的URL用于認證,用戶和密碼作為第4和第5個參數傳遞給open()方法
然后是send()方法,send()方法接受一個參數,即作為請求主題發送的數據。調用send()方法后,請求被分配到服務器。
如果是GET方法,send()方法無參數,或參數為null;如果是POST方法,send()方法的參數是要發送的數據。
xhr.open("GET","https://www.geekjc.com/fetch/library",true)
xhr.send(null);
2.3 接收響應
一個完整的http響應是有狀態嗎,響應頭集合,和響應主題組成。在收到響應的消息后,這些都是可以通過xhr對象的屬性個方法所使用,主要有一下4個屬性
- responseText;作為相應主題返回的文本,(文本形式)
- responseXML;如果響應的內容是‘text/xml’或者是哦application/xml;屬性中將會保存,響應數據的xml形式。DOM文檔形式。
- status: http的狀態碼(數字形式)
- statusText;http狀態說明,(文本形式)
收到響應后,第一步是檢查status,以確定響應已經成功的返回,一般來說。http狀態碼200作為成功的標志。此時,responseText屬性的內容也已經準備就緒,而且在內容正確的情況下,responseText的內容也可以訪問了,此外狀態碼304表示資源并沒有被修改,可以直接使用瀏覽器中的緩存。
無論響應內容是什么類型。都會被保存在responseText中,而對于非XML數據而言,responseText的值則為null;
響應也有同步和異步:
同步
如果接受的是同步響應,在在open()方法的第三個參數設置為false,那么send()方法將阻塞,直到請求完成,一旦send()返回,僅需檢查XHR對象的responseText屬性和status即可。
同步請求是吸引人的,但應該避免使用它們。客戶端javascript是單線程的,當send()方法阻塞時,它通常會導致整個瀏覽器UI凍結。如果連接的服務器響應慢,那么用戶的瀏覽器將凍結
例子:
<script>
function ajax(){
var xhr;
if(window.XMLHttpRequst){
var xhr = window.XMLHttpRequst;
}else {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
//發送請求
xhr.open("GET","/fetch/library",false);
xhr.send();
//同步接受響應
if(xhr.readyState==4){
if(xhr.status == 200){
result.innerHTML += xhr.responseText;
}
}
}
</script>
異步
如果接受的異步響應,這就需要檢測XHR的readyState的屬性,該屬性表示,請求/響應過程中當前的活動階段,這個屬性可以去一下的值,
- 0(UNSENT):未初始化。尚未調用open()方法
- 1(OPENED):啟動。已經調用open()方法,但尚未調用send()方法
- 2(HEADERS_RECEIVED):發送。己經調用send()方法,且接收到頭信息
- 3(LOADING):接收。已經接收到部分響應主體信息
- 4(DONE):完成。已經接收到全部響應數據,而且已經可以在客戶端使用了
理論上,只要readyState屬性的值由一個值變為另一個值,就會出發一次readyStatechange事件,可以利用這個事件來檢測每次狀態變化的readyState的值,我們對readyState值為4的階段感興趣,因為這時所有數據都已就緒,
例子:
<script>
function ajax(){
//創建xhr對象
var xhr;
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
}else{
xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
//異步接受響應
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status == 200){
//實際操作
result.innerHTML += xhr.responseText;
}
}
}
//發送請求
xhr.open('get','/fetch/post',true);
xhr.send();
}
</script>
3. 深入總結
3.1 中斷請求
核心:調用XMLHttpRequest對象上的abort方法
原生代碼示例:
xhr.open("POST","theUrl",true);
xhr.onreadystatechange=function(){
...//得到響應之后的操作
}
xhr.send();
//設置8秒鐘后檢查xmlHttp對象所發送的數據是否得到響應.
setTimeout("CheckRequest()","8000");
function CheckRequest(){
//為4時代表請求完成了
if(xhr.readyState!=4){
alert('響應超時');
//關閉請求
xhr.close();
}
}
這是手動的,也可以設置請求超時時間,讓它自己超時終止請求
超時
xhr.open("GET","https://www.geekjc.com/fetch/post",true);
xhr.ontimeout=function(){
console.log("請求超時")
}
xhr.timeout = 1000;
xhr.send();
這是設置1秒后,如果沒有數據響應,就停止請求了。
3.2 簡單封裝的ajax函數
ajax({
url: "./TestXHR.aspx", //請求地址
type: "POST", //請求方式
data: { name: "super", age: 20 }, //請求參數
dataType: "json",
success: function (response, xml) {
// 此處放成功后執行的代碼
},
fail: function (status) {
// 此處放失敗后執行的代碼
}
});
function ajax(options) {
options = options || {};
options.type = (options.type || "GET").toUpperCase();
options.dataType = options.dataType || "json";
var params = formatParams(options.data);
//創建 - 非IE6 - 第一步
if (window.XMLHttpRequest) {
var xhr = new XMLHttpRequest();
} else { //IE6及其以下版本瀏覽器
var xhr = new ActiveXObject('Microsoft.XMLHTTP');
}
//接收 - 第三步
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
var status = xhr.status;
if (status >= 200 && status < 300) {
options.success && options.success(xhr.responseText, xhr.responseXML);
} else {
options.fail && options.fail(status);
}
}
}
//連接 和 發送 - 第二步
if (options.type == "GET") {
xhr.open("GET", options.url + "?" + params, true);
xhr.send(null);
} else if (options.type == "POST") {
xhr.open("POST", options.url, true);
//設置表單提交時的內容類型
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(params);
}
}
//格式化參數
function formatParams(data) {
var arr = [];
for (var name in data) {
arr.push(encodeURIComponent(name) + "=" + encodeURIComponent(data[name]));
}
arr.push(("v=" + Math.random()).replace(".",""));
return arr.join("&");
}
4. 參考文章
- XMLHttpRequest 對象
- ajax
- 原生JS寫Ajax的請求函數-原生ajax(這篇文章寫得很好)