XHR 例項 GET 和 POST 非同步和同步

weixin_34357887發表於2018-09-18

1.實現Ajax


先來建立個XHR物件的例項:

var xhr = function(){
    if (window.XMLHttpRequest) {
        return new XMLHttpRequest();
    }else{
        return new ActiveObject('Micrsorf.XMLHttp');
    }
}();
console.log(xhr.readyState);

先來看個get請求

xhr.onreadystatechange = function(){
    switch(xhr.readyState){
        case 0 : 
            console.log(0,'未初始化....');
            break;
        case 1 : 
            console.log(1,'請求引數已準備,尚未傳送請求...');
            break;
        case 2 : 
            console.log(2,'已經傳送請求,尚未接收響應');
            break;
        case 3 : 
            console.log(3,'正在接受部分響應.....');
            data.innerHTML = xhr.responseText;
            break;
        case 4 : 
            console.log(4,'響應全部接受完畢');
            if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
                document.write(xhr.responseText);
            }else{
                document.write('error:' + xhr.status);
            }
            break;
    }
}
xhr.open('get','/products/getProduct?id=1');
xhr.send(null);

這裡傳送了個簡單的get非同步請求到我本地的web伺服器中,然後我們在控制檯看下輸出:


12653633-95a757c0af670515.png
2627979392-555ebbe4b1f51_articlex.png

可以看到剛建立完XHR物件後的readyState為0,當readyState為1,2,3,4,時都觸發了onreadystatechange事件,而 readyState為0沒有觸發,readyState為3時觸發了兩次,為什麼這樣呢?

為什麼為0時沒有觸發,我們剛建立XHR物件後readyState為0,然後接著執行後面的程式碼,直到send()方法之前readyState的值都沒有發生改變,所以在onreadystatechange事件中檢測readyState為0是沒有意義的。

readyState為3是觸發了兩次,其實有些請求不止兩次,看你請求的資料量的大小而定,我們增大接收資料來看看:


12653633-4adc4fe140ed343f.png
3729513477-555ecacca09e0_articlex.png

這就好了,既然我們能在readystate為3時獲取資料的相關資訊,那我們就可以利用這個特性在readystate為3時做一個資料載入進度條變化的效果了,這個講到XHR2物件的時候來試試。

還有一個就是在傳送get請求時,get請求的資料會附在URL之後(就是把資料放置在HTTP協議頭中),以?分割URL和傳輸資料,引數之間以&相連,如:'/products/getProduct?id=1'。如果資料是英文字母/數字,原樣傳送,如果是空格,轉換為+,如果是中文/其他字元,則直接把字串用BASE64加密,得出如:%E4%BD%A0%E5%A5%BD,其中%XX中的XX為該符號以16進製表示的ASCII。

POST把提交的資料則放置在是HTTP包的包體中。

再來個post請求

1.先來個簡單的表單,註冊一個使用者


12653633-e996180ff56e3cad.png
1719236697-555edbb6168b2_articlex.png

2.用Ajax提交資料到伺服器

var btn = document.getElementById('add');
btn.onclick = function(){
    var tel = document.getElementById('tel').value.toString(),
        pwd = document.getElementById('pwd').value.toString();
    var data =encodeFormData({
        tel : tel,
        pwd : pwd
    }) ;
    xhr.onreadystatechange = function(){
        switch(xhr.readyState){
            case 0 : 
                console.log(0,'未初始化....');
                break;
            case 1 : 
                console.log(1,'請求引數已準備,尚未傳送請求...');
                break;
            case 2 : 
                console.log(2,'正在新增....');
                break;
            case 3 : 
                console.log(3,'已接收資料長度:'+xhr.responseText.length );
                break;
            case 4 : 
                console.log(4,'響應全部接受完畢');
                if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
                    databox.innerHTML = xhr.responseText;
                }else{
                    databox.innerHTML = 'error:' + xhr.status;
                }
                break;
        }
    }
    xhr.open('post','/member/register');
    xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
    xhr.send(data);
 
};

那麼post請求需要注意兩個地方:
第一:

xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')

表單資料編碼格式有一個正式的MIME型別:pplication/x-www-form-urlencoded
當使用post方式提交這種順序表單時,必須設定Content-Type請求頭為這個值來模仿表單資料的提交。
第二:

var data =encodeFormData({
    tel : tel,
    pwd : pwd
}) ;

HTTP POST請求包含一個請求主體,它包含了客戶端傳送給伺服器的資料,比如:


12653633-139a197dde407f30.png
4010477523-555edfcf255dc_articlex.png

這裡為了簡單直接明文傳輸了。 這個資料比較少和簡單,我們也可以直接修改上面的send()方法傳送資料的方式如下:

xhr.send('tel='+tel+'&pwd='+pwd);

但是當這個表單資料的比叫多而複雜時,再以這種字串拼接的方式傳遞的話比較容易出錯,不好維護,所以我們需要封裝一個這樣的方法幫助我們將我們的資料拼接成這樣的格式:

function encodeFormData(data){
    if(!data) return '';
    var pairs = [];
    for(var name in data){
        if(!data.hasOwnProperty(name)) continue;
        if(typeof data[name] === 'function') continue;
        var value = data[name].toString();
        name = encodeURIComponent(name.replace('%20','+'));
        value = encodeURIComponent(value.replace('%20','+'));
        pairs.push(name+'='+value);
    }
    return pairs.join('&');
}

2.GET 還是 POST

get還是post,其實這和ajax是沒有關係的了,主要還是取決於這兩個請求方式的特點:

通過上面的兩個ajax的例項,我們可以看出get請求和post請求的一些特點:

  • get請求:
    • GET 請求可被快取
    • GET 請求保留在瀏覽器歷史記錄中
    • GET 請求可被收藏為書籤
    • GET 請求不應在處理敏感資料時使用
    • GET 請求有長度限制
    • GET 請求只應當用於取回資料
  • post請求:
    • POST 請求不會被快取
    • POST 請求不會保留在瀏覽器歷史記錄中
    • POST 不能被收藏為書籤
    • POST 請求對資料長度沒有要求
      那,有了這個比較,你應該知道什麼時候用get什麼時候用post了。

3.非同步和同步

ajax預設的都是非同步的請求,我們上面的兩個例項也是用的非同步請求,那沒什麼不用同步呢?同步和非同步有什麼特點?
同步請求:
傳送器請求-->等待結果-->操作結果-->繼續還行後面的程式碼 ,這是同步請求的大致過程,由於客服端的javascript是單執行緒的,也就是說我們必須等待結果完全接收完畢之後才能繼續執行後面的程式碼,嚴格按照步驟一步一步來,它通常會導致整個瀏覽器的UI阻塞(白屏等),如果連線伺服器響應很慢,那麼使用者瀏覽器將凍結,用不不能進行其他操作。
如果我們發起一個同步請求,chrome瀏覽器會給你這樣一個警告:Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check http://xhr.spec.whatwg.org/. 意思就是同步請求不利於使用者體驗。
非同步請求
傳送器請求-->繼續還行後面的程式碼-->響應結果接收完畢了-->操作結果,這是同步請求的大致過程。
可以看到,非同步請求在傳送請求之後沒有等待結果的返回而是繼續執行後面的程式碼,也就是說在結果返回之前使用者可以操作其他東西或是看到其他UI,使用者體驗良好。但是有些情況下我們還是得用同步請求。

相關文章