深刻理解ajax系列第一篇——XHR對象

前面的話

  1999年,微軟公司發佈IE5,第一次引入新功能:容許javascript腳本向服務器發起HTTP請求。這個功能當時並無引發注意,直到2004年Gmail發佈和2005年Google Map發佈,才引發普遍重視。2005年2月,ajax這個詞第一次正式提出,指圍繞這個功能進行開發的一整套作法。今後,ajax成爲腳本發起HTTP通訊的代名詞,W3C也在2006年發佈了它的國際標準。本文是ajax系列的第一篇——XHR對象javascript

 

概述

  ajax是asynchronous javascript and XML的簡寫,中文翻譯是異步的javascript和XML,這一技術可以向服務器請求額外的數據而無須卸載頁面,會帶來更好的用戶體驗。雖然名字中包含XML,但ajax通訊與數據格式無關php

  ajax包括如下幾步驟:一、建立AJAX對象;二、發出HTTP請求;三、接收服務器傳回的數據;四、更新網頁數據html

  歸納起來,就是一句話,ajax經過原生的XMLHttpRequest對象發出HTTP請求,獲得服務器返回的數據後,再進行處理前端

 

建立

  ajax技術的核心是XMLHttpRequest對象(簡稱XHR),這是由微軟首先引入的一個特性,其餘瀏覽器提供商後來都提供了相同的實現。XHR爲向服務器發送請求和解析服務器響應提供了流暢的接口,可以以異步方式從服務器取得更多信息,意味着用戶單擊後,能夠沒必要刷新頁面也能取得新數據java

  IE5是第一款引入XHR對象的瀏覽器。在IE5中,XHR對象是經過MSXML庫中的一個ActiveX對象實現的,而IE7+及其餘標準瀏覽器都支持原生的XHR對象ajax

  建立一個XHR對象,也叫實例化一個XHR對象,由於XMLHTTPRequest()是一個構造函數。下面是建立XHR對象的兼容寫法數據庫

var xhr;
if(window.XMLHttpRequest){
    xhr = new XMLHttpRequest();
}else{
    xhr = new ActiveXObject('Microsoft.XMLHTTP');
}

  [注意]若是要創建N個不一樣的請求,就要使用N個不一樣的XHR對象。固然能夠重用已存在的XHR對象,但這會終止以前經過該對象掛起的任何請求json

 

發送請求

open()後端

  在使用XHR對象時,要調用的第一個方法是open(),以下所示,該方法接受3個參數瀏覽器

xhr.open("get","example.php", false);

  一、open()方法的第一個參數用於指定發送請求的方式,這個字符串,不區分大小寫,但一般使用大寫字母。"GET"和"POST"是獲得普遍支持的

  "GET"用於常規請求,它適用於當URL徹底指定請求資源,當請求對服務器沒有任何反作用以及當服務器的響應是可緩存的狀況下

  "POST"方法經常使用於HTML表單。它在請求主體中包含額外數據且這些數據常存儲到服務器上的數據庫中。相同URL的重複POST請求從服務器獲得的響應可能不一樣,同時不該該緩存使用這個方法的請求

  除了"GET"和"POST"以外,參數還能夠是"HEAD"、"OPTIONS"、"PUT"。而因爲安全風險的緣由,"CONNECT"、"TRACE"、"TRACK"被禁止使用

  [注意]關於HTTP協議8種經常使用方法的詳細介紹移步至此

  二、open()方法的第二個參數是URL,該URL相對於執行代碼的當前頁面,且只能向同一個域中使用相同端口和協議的URL發送請求。若是URL與啓動請求的頁面有任何差異,都會引起安全錯誤

  三、open()方法的第三個參數是表示是否異步發送請求的布爾值,若是不填寫,默認爲true,表示異步發送

  四、若是請求一個受密碼保護的URL,把用於認證的用戶名和密碼做爲第4和第5個參數傳遞給open()方法

send()

  send()方法接收一個參數,即要做爲請求主體發送的數據。調用send()方法後,請求被分派到服務器

  若是是GET方法,send()方法無參數,或參數爲null;若是是POST方法,send()方法的參數爲要發送的數據

xhr.open("get", "example.txt", false);
xhr.send(null);

 

接收響應

  一個完整的HTTP響應由狀態碼、響應頭集合和響應主體組成。在收到響應後,這些均可以經過XHR對象的屬性和方法使用,主要有如下4個屬性

responseText: 做爲響應主體被返回的文本(文本形式)
responseXML: 若是響應的內容類型是'text/xml'或'application/xml',這個屬性中將保存着響應數據的XML DOM文檔(document形式)
status: HTTP狀態碼(數字形式)
statusText: HTTP狀態說明(文本形式)

  在接收到響應後,第一步是檢查status屬性,以肯定響應已經成功返回。通常來講,能夠將HTTP狀態碼爲200做爲成功的標誌。此時,responseText屬性的內容已經就緒,並且在內容類型正確的狀況下,responseXML也能夠訪問了。此外,狀態碼爲304表示請求的資源並無被修改,能夠直接使用瀏覽器中緩存的版本;固然,也意味着響應是有效的

  不管內容類型是什麼,響應主體的內容都會保存到responseText屬性中,而對於非XML數據而言,responseXML屬性的值將爲null

if((xhr.status >=200 && xhr.status < 300) || xhr.status == 304){
    alert(xhr.responseText);
}else{
    alert('request was unsuccessful:' + xhr.status);
}

 

同步

  若是接受的是同步響應,則須要將open()方法的第三個參數設置爲false,那麼send()方法將阻塞直到請求完成。一旦send()返回,僅須要檢查XHR對象的status和responseText屬性便可

  同步請求是吸引人的,但應該避免使用它們。客戶端javascript是單線程的,當send()方法阻塞時,它一般會致使整個瀏覽器UI凍結。若是鏈接的服務器響應慢,那麼用戶的瀏覽器將凍結

<button id="btn">獲取信息</button>
<div id="result"></div>
<script>
btn.onclick = function(){
    //建立xhr對象
    var xhr;
    if(window.XMLHttpRequest){
        xhr = new XMLHttpRequest();
    }else{
        xhr = new ActiveXObject('Microsoft.XMLHTTP');
    }
    //發送請求
    xhr.open('get','/uploads/rs/26/ddzmgynp/message.xml',false);
    xhr.send();
    //同步接受響應
    if(xhr.readyState == 4){
        if(xhr.status == 200){
            //實際操做
            result.innerHTML += xhr.responseText;
        }
    }
}
</script>    
//message.xml
<p>hello world</p>

 

異步

  若是須要接收的是異步響應,這就須要檢測XHR對象的readyState屬性,該屬性表示請求/響應過程的當前活動階段。這個屬性可取的值以下:

0(UNSENT):未初始化。還沒有調用open()方法
1(OPENED):啓動。已經調用open()方法,但還沒有調用send()方法
2(HEADERS_RECEIVED):發送。己經調用send()方法,且接收到頭信息
3(LOADING):接收。已經接收到部分響應主體信息
4(DONE):完成。已經接收到所有響應數據,並且已經能夠在客戶端使用了

  理論上,只要readyState屬性值由一個值變成另外一個值,都會觸發一次readystatechange事件。能夠利用這個事件來檢測每次狀態變化後readyState的值。一般,咱們對readyState值爲4的階段感興趣,由於這時全部數據都已就緒

  [注意]必須在調用open()以前指定onreadystatechange 事件處理程序才能確保跨瀏覽器兼容性,不然將沒法接收readyState屬性爲0和1的狀況

xhr.onreadystatechange = function(){
    if(xhr.readyState === 4){
        if(xhr.status == 200){
            alert(xhr.responseText);
        }
    }
}
<button id="btn">獲取信息</button>
<div id="result"></div>
<script>
btn.onclick = function(){
    //建立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','message.xml',true);
    xhr.send();
}
</script>    
//message.xml
<p>hello world</p>

超時

  XHR對象的timeout屬性等於一個整數,表示多少毫秒後,若是請求仍然沒有獲得結果,就會自動終止。該屬性默認等於0,表示沒有時間限制

  若是請求超時,將觸發ontimeout事件

  [注意]IE8-瀏覽器不支持該屬性

xhr.open('post','test.php',true);
xhr.ontimeout = function(){
    console.log('The request timed out.');
}
xhr.timeout = 1000;
xhr.send();

 

優化

  使用AJAX接收數據時,因爲網絡和數據大小的緣由,並非馬上就能夠在頁面中顯示出來。因此,更好的作法是,在接受數據的過程當中,顯示一個相似loading的小圖片,而且禁用按鈕;當數據徹底接收後,再隱藏該圖片,並啓用按鈕

<button id="btn">獲取信息</button>
<img id="img" height="16" style="display:none" src="data:image/gif;base64,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" alt="loading">
<div id="result"></div>
<script>
var add = (function(){
    var counter = 0;
    return function(){
        return ++counter;
    }
})();
btn.onclick = function(){
    img.style.display = 'inline-block';
    btn.setAttribute('disabled','');
    //建立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){
              img.style.display = 'none';
              btn.removeAttribute('disabled');
              var data = JSON.parse(xhr.responseText);
              var sum = add() - 1;
              if(sum < data.length){
                result.innerHTML += data[sum];    
              }
            }
        }
    }
    //發送請求
    xhr.open('get','data.php',true);
    xhr.send();
}
</script>      
<?php
echo json_encode([1,2,3,4,5]);
?>

 

最後

  經過實例的演示發現,ajax前端自己的內容並不難。可是,因爲ajax涉及到一些後端及網絡的知識,使得學起來不是很容易。之後的博文將逐步深刻地介紹ajax的重點內容

相關文章
相關標籤/搜索