天天看點

AJAX(3)XMLHttpRequest對象簡介實作Ajax的GET請求實作Ajax的POST請求對請求參數進行編碼readyState屬性onreadystatechange事件設定HTTP請求時限xhr2新增事件FormData對象上傳檔案并顯示進度條

XMLHttpRequest對象簡介

綱要

XMLHttpRequest

是一個内建的浏覽器對象,Ajax技術的核心就是

XMLHttpRequest

對象。

jQuery中的

$.ajax()

$.get()

$.post()

的底層實作,就是

XMLHttpRequest

曆史

  • XMLHttpRequest對象最初是 WHATWG(超文本應用程式技術工作組) 的一部分。
  • 2006年移至W3C。
  • 2008年2月,對XMLHttpRequest進行了擴充(如進度事件和跨域請求),也就是所謂的XMLHttpRequest Level 2,直至2011年底。
  • 2012年底,它移回了 WHATWG。

小結

  • XMLHttpRequest 簡稱 XHR 對象,是一個浏覽器内置對象,目前,所有的浏覽器均支援這個對象。
  • 它的作用是可以實作Ajax請求,Ajax技術的核心就是

    XMLHttpRequest

    對象。

實作Ajax的GET請求

步驟

  1. 建立xhr對象。
  2. 注冊 xhr.onreadystatechange 事件。在 readystatechange 函數中,接收響應結果。
  3. 調用open方法,初始化一個請求,此方法用于配置請求方式和url。
  4. 調用send方法,發送請求。

基礎代碼

// 1. 建立xhr對象
var xhr = new XMLHttpRequest();
​
// 2. 注冊 xhr.onreadystatechange 事件,當Ajax請求**成功**後,會觸發onload函數。在 readystatechange 函數中,接收響應結果。
xhr.onreadystatechange = function () {
    // 使用 xhr.response 接收響應結果
    var res = xhr.responseText;
}
​
// 3. 調用open方法,設定請求方式及請求的url位址
// xhr.open('GET', 'http://www.itcbc.com:3006/api/getbooks');
xhr.open('GET', 'http://www.itcbc.com:3006/api/getbooks?appkey=13200008888');
​
// 4. 最後,調用send方法,發送這次ajax請求
xhr.send();      

請求參數

重要:如果傳遞請求參數,請求參數要以查詢字元串形式拼接到url後面。

形如:

url?id=1&appkey=13200008888

GET和DELETE請求,需要以這種方式來傳遞參數。

其他說明

API 相容性
XMLHttpRequest IE7+ 支援
open 所有浏覽器均支援
send 所有浏覽器均支援
onreadystatechange 所有浏覽器均支援
responseText 所有浏覽器均支援

了解

XMLHttpRequest

對象所有API的相容性,點選這裡。

實作Ajax的POST請求

步驟

(比GET請求多了一行代碼)

  1. 建立xhr對象。
  2. 注冊 xhr.onreadystatechange 事件。在 readystatechange 函數中,接收響應結果。
  3. 調用open方法,初始化一個請求,此方法用于配置請求方式和url。
  4. 調用setRequestHeader方法,設定請求頭。
  5. 調用send方法,發送請求。

基礎代碼

// 1. 建立xhr對象
var xhr = new XMLHttpRequest();
​
// 2. 注冊 xhr.onreadystatechange 事件,當Ajax請求**成功**後,會觸發onload函數。在 readystatechange 函數中,接收響應結果。
xhr.onreadystatechange = function () {
    // 使用 xhr.response 接收響應結果
    var res = xhr.responseText;
}
​
// 3. 調用open方法,設定請求方式及請求的url位址
xhr.open('POST', 'http://www.itcbc.com:3006/api/addbook');
​
// 4. 調用setRequestHeader,設定請求頭,目的是告知伺服器以何種方式解析請求體
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
​
// 5. 最後,調用send方法,發送這次ajax請求
xhr.send('bookname=西遊記&author=唐僧&publisher=大唐出版社&appkey=13200008888');      

請求體

重申一下,POST請求的參數,稱之為請求體。

和GET請求一樣,請求體也要寫成查詢字元串格式,不同之處是,請求體做為send方法的參數發送。

其他說明

API 相容性
setRequestHeader 所有浏覽器均支援

setRequestHeader

方法用于設定請求頭,格式如下:

xhr.setRequestHeader('名稱', '值');      
  • 設定的請求頭可以在 network 面闆 > Headers > Request Headers 中檢視。
  • 大部分請求頭由浏覽器管理,不允許我們修改。是以我們無需關心。
  • 一旦設定了請求頭,就無法撤銷了。

Content-Type

  • 隻有POST、PUT請求,才需要設定

    Content-Type

    請求頭
  • 這裡設定的

    Content-Type

    ,作用是告知伺服器,浏覽器送出的資料是何種類型的。
    • 值為:application/x-www-form-urlencoded(需要自己指定) ,表示用戶端送出的是查詢字元串。
    • 值為:application/json(需要自己指定) ,表示用戶端送出的是 JSON 字元串。
    • 值為:multipart/form-data(xhr對象會自動設定),表示用戶端送出的是 FormData 對象。

對請求參數進行編碼

什麼是URL編碼?

  • 把中文和部分特殊符号轉成 URL的标準格式 ,這就是url編碼
  • 如,把“中文” 進行url編碼後得到 “%E4%B8%AD%E6%96%87”

為什麼要對請求參數進行編碼

  • RFC文檔規定,隻有字母和數字[0-9a-zA-Z]、一些特殊符号“$-_.+!*’(),”[不包括雙引号]、以及某些保留字,才可以不經過編碼直接用于URL。
  • 對于Unicode字元,RFC文檔建議使用utf-8對其進行編碼得到相應的位元組,然後對每個位元組執行百分号編碼,也就是所謂的 URL編碼。

編碼能夠解決的問題

  • 亂碼問題(浏覽器使用utf-8、伺服器使用其他編碼,傳輸資料的時候,就會亂碼)
  • 送出的内容有特殊符号問題,比如添加一本書,書名是 “紅&黑”

如何進行URL編碼

JS提供了内置編碼解碼函數

  • 編碼:encodeURIComponent('西遊記')
  • 解碼:decodeURIComponent("%E8%A5%BF%E6%B8%B8%E8%AE%B0");
具體檢視手冊:JavaScript 全局參考手冊

小結

無論是GET請求還是POST請求,向伺服器傳遞參數的時候,必須要對參數進行編碼。這裡的參數包括參數名和值。

比如,參數為

shu ming=紅&黑

  • 參數名中有空格,需要編碼;
  • 值有中文,且有歧義,需要編碼;

readyState屬性

Ajax從建立xhr對象開始,一直到完全接收伺服器傳回的結果為止;我們可以把整個請求響應過程劃分為5個階段。并且可以使用 xhr.readyState 屬性檢測目前請求執行到哪個階段了。

readyState屬性值為一個數字,不同的數字表示Ajax的不同狀态。

  • 如果狀态值為0(xhr.readyState === 0),初始狀态,表示xhr對象一定建立了。
  • 如果狀态值為1(xhr.readyState === 1),表示open一定調用了
  • 如果狀态值為2(xhr.readyState === 2),表示send一定調用了,并且已經接收到響應頭。
  • 如果狀态值為3(xhr.readyState === 3),表示正在接收伺服器傳回的資料(可能已接收完畢,也可能正在接收中,取決于資料量的大小)
  • 如果狀态值為

    4

    xhr.readyState === 4

    ),表示Ajax請求~響應過程完成
值為0、1、2、3的時候,基本上不用關心。主要關心值為4的時候,因為這個時候表示Ajax請求~響應過程完成了,如果成功的結束了,則可以完全接收伺服器響應的結果。

下面在建立xhr對象後和onreadystatechange事件内部分别輸出 xhr.readyState 可以分别得到 0 、1、2、3、4。

// 1. 建立xhr對象
var xhr = new XMLHttpRequest();
​
console.log(xhr.readyState); // ===> 0
​
// 2. 注冊 xhr.onreadystatechange 事件,當Ajax請求**成功**後,會觸發onload函數。在 readystatechange 函數中,接收響應結果。
xhr.onreadystatechange = function () {
    console.log(xhr.readyState); // ===> 1/2/3/4
    // 使用 xhr.response 接收響應結果
    var res = xhr.responseText;
}
​
// 3. 調用open方法,設定請求方式及請求的url位址
// xhr.open('GET', 'http://www.itcbc.com:3006/api/getbooks');
xhr.open('GET', 'http://www.itcbc.com:3006/api/getbooks?appkey=13200008888');
​
// 4. 最後,調用send方法,發送這次ajax請求
xhr.send();      

onreadystatechange事件

介紹

onreadystatechange 翻譯過來是 當Ajax的請求狀态改變的時候。

是以,它是配合上述的 readyState 使用的事件。

事件具體的觸發時機如下:

  • readyState屬性值改變的時候
    • 0 --> 1
    • 1 --> 2
    • 2 --> 3
    • 3 --> 4
  • 接收到的資料量改變的時候,此時 readyState 的值保持為3,但也會觸發 onreadystatechange 事件(發生在分塊接收大量資料的時候)
是以,綁定的onreadystatechange事件,可能會觸發多次。

完整代碼

前文提到,onload有浏覽器相容問題,如果你的項目需要支援低版本的浏覽器,那麼可以使用 onreadystatechange事件代替onload事件。

由于onreadystatechange事件可能會觸發多次,是以需要在事件中加入判斷,已保證準确的接收到響應結果。

// 1. 建立xhr對象
var xhr = new XMLHttpRequest();
// IE6 建立對象 var xhr = new ActiveXObject('Microsoft.XMLHTTP');
​
// 2. 使用onreadystatechange代替onload
xhr.onreadystatechange = function () {
    // 判斷Ajax請求是否完成
    if(xhr.readyState === 4) {
        // 還要根據響應狀态碼判斷,請求是否成功
        if (xhr.status === 200) {
            console.log(xhr.responseText);
        } else {
            console.log('請求失敗')
        }
    }
}
​
// 3. 調用open方法,設定請求方式及請求的url位址(如有參數,拼接到url後面)
xhr.open('GET', 'http://www.itcbc.com:3006/api/getbooks?appkey=13200008888');
​
// 4. 最後,調用send方法,發送這次ajax請求
xhr.send();      

設定HTTP請求時限

  • timeout -- Level 2 新增,IE8+支援,用于設定請求的逾時時間,機關是毫秒
  • ontimeout -- Level 2 新增,IE10+支援,如果請求逾時了,會觸發 ontimeout 事件
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
  if (this.readyState === 4 && this.status === 200) {
    console.log(this.responseText);
  }
}
xhr.timeout = 30; // 機關是毫秒
// 當請求逾時之後,會觸發下面的函數
xhr.ontimeout = function () {
    alert('請求逾時,請重新整理重試');
}
xhr.open('GET', 'http://www.itcbc.com:3006/api/getbooks?appkey=13200008888');
xhr.send();      

xhr2新增事件

  • ontimeout -- 請求逾時之後,觸發的事件
  • onload -- 請求成功後觸發的事件(因為已經表示成功後觸發了,是以事件内部就不要加if判斷了)
  • onerror -- 請求失敗後觸發的事件,比如網絡不通造成的請求發送失敗
  • onloadstart -- 請求開始的時候,觸發的事件
  • onloadend -- 請求完成後觸發的事件,這裡的完成包括成功和失敗
  • onprogress -- 用戶端下載下傳(接收)資料的時候,觸發的事件

FormData對象

介紹

Form是表單,Data是資料。猜測,它和表單資料有關系。

FormData是h5出現之後,新增的一個對象。用于管理表單資料。IE10+支援。

建立的FormData對象,可直接通過

xhr.send(FormData對象)

送出給伺服器的接口。

基本文法

var fd = new FormData([form]); // 參數是表單的DOM對象,可選      

FormData的API:(除了append方法IE10支援外,其他方法IE均不支援)

  • append('key', 'value');

    -- 向對象中追加資料
  • set('key', 'value');

    -- 修改對象中的資料
  • delete('key'); -- 從對象中删除資料
  • get('key') -- 擷取對象中的資料
  • getAll('key') -- 擷取指定key的全部資料
  • forEach() -- 周遊對象中的資料
  • ....
<form action="">
    <input type="text" name="username"><br />
    <input type="password" name="pwd"><br />
    <input type="radio" name="sex" value="nan">
    <input type="radio" name="sex" value="nv"><br />
​
    <button>送出</button>
</form>
​
<script>
    document.querySelector('form').onsubmit = function (e) {
        e.preventDefault();
​
        // 使用FormData收集表單資料
        var fd = new FormData(this); // 傳入表單的DOM對象
​
        // append向fd對象中追加資料
        fd.append('age', 20); // 追加一個age
        fd.append('username', 'lisi'); // 追加一個username
​
        // set修改fd中的資料
        fd.set('sex', 'yao'); // 修改sex
​
        // get用于擷取一個值
        console.log(  fd.get('username')  ); // 擷取到一個username的值
        console.log(  fd.getAll('username')  ); // 擷取到全部username的值
​
        // console.log(fd); // 輸出fd沒用,看不到資料
        // 隻能通過forEach來檢查對象中有哪些資料
        fd.forEach(function (val, key) {
            // console.log(key, val);
        });
​
    }
</script>      

送出FormData

FormData本就是配合XHR對象一起使用的

前面我們收集到了很多值,現在,我們可以通過Ajax,将這些值送出給接口。

  • 隻能通過POST方式送出FormData對象
  • 不能設定Content-Type請求頭,因為當送出FormData的時候,浏覽器會自動設定這個請求頭。
<form action="">
    姓名:<input type="text" name="username"><br>
    年齡:<input type="text" name="age"><br>
    身高:<input type="text" name="height"><br>
    <button>送出</button>
</form>

<script>

    document.querySelector('form').onsubmit = function (e) {
        e.preventDefault();
        var fd = new FormData(this);

        // ajax送出資料到接口
        var xhr = new XMLHttpRequest();
        xhr.onload = function () {
            console.log(this.responseText);
        }
        xhr.open('POST', 'http://www.itcbc.com:3006/api/formdata');
        // xhr.setRequestHeader(); // 使用FormData,不要設定請求頭;寫了請求頭,反而會報錯
        xhr.send(fd);
    }
</script>      
如果送出FormData資料給接口,需要接口的支援。比如添加圖書、添加評論接口都不支援送出FormData資料。

注意事項(必看)

  • 使用FormData,要求表單各項必須有name屬性,因為FormData也是根據表單各項的name屬性擷取值的
  • 不能收集禁用狀态的值。
  • 執行個體化 FormData對象,傳入表單的DOM對象,可以快速收集到表單各項值。
  • 可以收集檔案資訊。可以完成檔案上傳。
  • 如果要檢查FormData中有哪些值,需要使用forEach周遊。
  • 如果需要動态添加或修改FormData中的值,可以調用 FormData的append或set方法。

上傳檔案并顯示進度條

實作過程:

  1. 先完成送出檔案,即使用FormData擷取檔案域的内容,然後Ajax送出FormData對象給接口。
  2. 再能夠上傳檔案的基礎之上,注冊xhr.upload.onprogress事件,監聽上傳進度變化,制作進度條

參考代碼:

<form>
	<input type="file" name="avatar">
	<button>上傳</button>
</form>

<!-- 設定一個進度條,這裡使用h5新标簽,開始隐藏 -->
<progress max="0" value="0" style="display: none"></progress>


<script>
    document.querySelector('form').onsubmit = function (e) {
        e.preventDefault();
        // 使用FormData收集輸入框的值(這裡是選擇的圖檔)
        var fd = new FormData(this);

        // ajax送出fd對象
        // 接口文檔,檢視ppt即可
        var xhr = new XMLHttpRequest();
        
        /*************************************************/
        /*↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓*/
        // 找到 進度條标簽 
        var progress = document.querySelector('progress');
        // 注冊上傳監聽事件
        xhr.upload.onprogress = function (e) {
            progress.style.display = 'block';
            progress.max = e.total; // 檔案總大小,機關位元組
            progress.value = e.loaded; // 已經上傳了多少位元組
        }
        /*↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑*/
        /************************************************/
        
        xhr.onload = function () {
            console.log(this.responseText);
        }
        xhr.open('POST', 'http://www.itcbc.com:3006/api/formdata');
        xhr.send(fd);

    }
</script>      
注意,上傳進度使用xhr.upload.onprogress事件;下載下傳進度使用xhr.onprogress事件;

繼續閱讀