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請求
步驟
- 建立xhr對象。
- 注冊 xhr.onreadystatechange 事件。在 readystatechange 函數中,接收響應結果。
- 調用open方法,初始化一個請求,此方法用于配置請求方式和url。
- 調用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請求多了一行代碼)
- 建立xhr對象。
- 注冊 xhr.onreadystatechange 事件。在 readystatechange 函數中,接收響應結果。
- 調用open方法,初始化一個請求,此方法用于配置請求方式和url。
- 調用setRequestHeader方法,設定請求頭。
- 調用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
),表示Ajax請求~響應過程完成xhr.readyState === 4
值為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方法。
上傳檔案并顯示進度條
實作過程:
- 先完成送出檔案,即使用FormData擷取檔案域的内容,然後Ajax送出FormData對象給接口。
- 再能夠上傳檔案的基礎之上,注冊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事件;