AJAX-XMLHttpRequest

一. AJAX是什麼?

AJAX是js腳本向服務器發起http通訊的統稱。html

總的來講,ajax就是js經過XMLHttpRequest建立實例,發起請求,接受請求,更新頁數據的過程。ajax

二.XMLHttpRequest

1.什麼是XMLHttpRequest?

XMLHttpRequest是一個位於Window對象上的一個原生的構造函數。json

它是ajax通訊的主要接口,用於瀏覽器和服務器之間通訊。跨域

經過它生成的實例對象,能夠用於發起http請求:數組

const xhr = new XMLHttpRequest();

因爲位於Window對象,因此實例化的xhr對象其實就是一個瀏覽器內置的對象。瀏覽器

它提供了對http協議的徹底的訪問,用於JS進行http請求和http響應。安全

2.兼容性

幾乎全部的主流瀏覽器都支持,IE須要經過下面的辦法兼容服務器

 new ActiveXObject("Microsoft.XMLHTTP")

3.發展歷史

XMLHttpRequest有兩個階段,一個是Level1,一個是Leve2cookie

level1的缺點:網絡

  • 受同源策略(Same Origin Policy)影響, 不能發送跨域請求
  • 不能發送二進制(音頻,視頻,圖片等)文件,只能發送文本
  • 發送和接收數據時,沒有實時進度信息,只能判斷是否完成

level2解決了以上缺點,並添加了新功能:

  • 只要服務器容許,能夠進行跨域訪問
  • 能夠發送和獲取二進制數據(音頻,視頻,圖片等都是二進制文件)
  • 能夠獲取請求發送數據的實時進度信息
  • 新增FormData數據,能夠發送表單數據(multipart/formdata)
  • 能夠設置過期時間timeout

4.只讀屬性

1.readyState --HTTP 請求的狀態.

從對象實例化爲0開始到結束爲4,依次遞增。

狀態 名稱 描述
0 UNSENT 初始化。new了實例或者已經被abort()方法重置
1 OPENED 調用了open方法,可是還未調用send方法。請求還未發送。
2 HEADERS_RECEIVED send發送成功。而且接收到服務器返回的請求頭和狀態碼
3 LOADING 響應頭已經接收。響應體開始接收,未完成。
4 DONE 響應接收完成。

 

 

 

 

 

 

每次狀態改變都會觸發onreadystatechange事件。

 LOADING狀態可能會觸發屢次,由於每接收一個數據包就會觸發一次。

2.status--http請求響應中~的狀態碼

進入響應前和響應失敗都是0;響應中若是未特殊指定,都爲200。

let xhr = null;
if (window.XMLHttpRequest) {// code for Firefox, Chrome, etc.
    xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) {// code for IE
    xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
console.log('UNSENT-->',xhr.readyState,'-->', xhr.status,'-->',xhr.statusText);
if (xhr != null) {
    let data = null
    xhr.open('GET','./1.jpeg', true);
    console.log('OPENED-->',xhr.readyState,'-->', xhr.status,'-->',xhr.statusText);
    xhr.send(data);    
}
xhr.onreadystatechange = function() { // 只有在readyState >=3 的時候,才能獲取正確的結果。
    console.log('狀態變化-->',xhr.readyState,'-->',xhr.status,'-->',xhr.statusText)
}
xhr.onprogress = function () {
    console.log('LOADING',xhr.readyState,'-->', xhr.status,'-->',xhr.statusText);
};
xhr.onload = function () {
    console.log('DONE',xhr.readyState,'-->', xhr.status,'-->',xhr.statusText);
};
// 運行結果以下:
/*
UNSENT-->0 -->0 -->""
OPEND-->1-->0-->""
狀態變化-->1->0-->""
狀態變化-->2-->200-->"OK" //headers_received
狀態變化-->3-->200-->"OK" //loading
LOADING-->3-->200-->"OK" 
LOADING-->4-->200-->"OK"
狀態變化-->4-->200-->"OK" //done
DONE-->4-->200-->"OK"
*/

3. statusText -- http響應中~的狀態名稱

例如:xhr.statusText --- OK 

4. response--返回響應

若是responseType的類型是""或者"text",那麼LOADING狀態時,會接收到傳遞的部分數據。

5. responseText

從服務器返回的字符串;當readyState爲4時,取到完整的字符串。爲3時,可能返回部分數據。

6. responseXML

從服務器返回的XML/HTML文檔;要求返回的Content-Type: text/xml 或者application/xml。

該屬性生效的前提是:

xhr.responseType = "document";

若是仍不符合要求:

xhr.overrideMimeType('text/xml');

7.responseURL

返回實際返回數據(跳轉則爲跳轉後的網址)的服務器的地址;若是有錨點(fragment),會自動剝離錨點。

5.可寫屬性

1.responseType --- 指定服務器應該返回的數據類型

  • "": 默認。 返回字符串,等同於"text"類型。
  • "text": 返回字符串。
  • "arraybuffer": 返回ArrayBuffer類型的數據(二進制數組)。
  • "blob": 返回Blob數據,二進制對象,如圖片。
  • "document": 返回Document對象,文檔對象,XML或者HTML文檔。
  • "json": 返回JSON對象。
xhr.responseType = 'json';
// 若是設置爲json,瀏覽器會自動調用JSON.parse()方法;
// 即經過xhr.response返回的是一個JSON對象

若是responstType指定的數據,服務器沒法返回,能夠經過xhr.overrideMimeType()來

覆蓋服務器返回的數據類型。客戶端根據該方法的參數解析

// 該方法必須在send()方法以前調用
xhr.overrideMimeType('text/plain');

2.timeout--設置請求超時時間

默認0,表示不設置請求超時。

其餘數值,單位爲ms, 規定時間後觸發ontimeout事件。

3.withCredentials--攜帶驗證信息

默認false; 該屬性只跨域請求時須要設置。

同域請求時,瀏覽器會自動將cookie添加到請求頭中,可是跨域請求不會。

由於CORS規定,跨域時,不容許攜帶任何認證信息,除非withCredentials=true;

首先,爲了可以跨域,服務器端須要設置

Access-Control-Allow-Origin: *// 或者具體的域名

若是請求須要認證信息,瀏覽器端須要設置:

xhr.withCredentials = true

服務器端須要設置

Access-Control-Allow-Origin: //具體的origin,必定不能設置成*
Access-Control-Allow-Credentials: true

6.方法

1.open(method, url [, async, username, password])方法

初始化請求參數

method: 請求方式主要有GET、POST、PUT、DELETE,也能夠是HEAD(請求資源的頭部信息,和GET返回的頭部同樣)---CORS安全

url: 請求路由

async: 默認true,異步請求。爲false時,同步, 已被廢棄deprecated

基本都用異步請求。同步請求幾乎不用,一是由於會出現阻塞,網頁掛起的狀況;二是不能使用timeout,不能進行跨域,也沒有進度信息。

const xhr = new XMLHttpRequest();
xhr.open("POST", url, true); // 異步
xhr.send();
// readyState === 1 異步請求send當即返回

const xhr = new XMLHttpRequest();
xhr.open("POST", url, false); // 同步
xhr.send();
// readyState === 4 同步等全部的進程結束再返回

username,password: 認證權限

2. setRequestHeader(name, value)

只能在open和send之間調用。既能夠設置系統默認的一些頭部信息(如ContentType),也能夠設置自定義的頭部信息(‘X-USER’)。

一旦設置,就沒法撤銷。設置一樣的name,也是append,而不會覆蓋。

var client = new XMLHttpRequest();
client.open('GET', 'demo.cgi');
client.setRequestHeader('X-Test', 'one'); // 自定義請求頭
client.setRequestHeader('X-Test', 'two');  // 自定義請求頭
client.send();
// X-TEST: one, two

name: 請求頭的名稱。忽略大小寫。

有些請求頭只能由瀏覽器控制,用戶不能操做。不然  Refused to set unsafe header "..."

`Accept-Charset` `Accept-Encoding`
`Access-Control-Request-Headers` `Access-Control-Request-Method`
`Connection`
`Content-Length` `Cookie` `Cookie2` `Date`
`DNT`
`Expect`
`Host` `Keep-Alive` `Origin` `Referer`
`TE`
`Trailer`
`Transfer-Encoding`
`Upgrade`
`Via`

用戶能夠操做的請求頭有:

Accept: 經過設置Accept通知服務器客戶端能夠接受的數據類型。

Content-Type: 設置發送的數據類型,即send(body)中請求體的類型。如下所有爲POST請求

      GET方法設置這些請求頭無效

  • "multipart/formdata; boundry=something": 當傳遞FormData類型時,默認是該類型。

      // 示例: multipart/form-data; boundary=----WebKitFormBoundary2OgAPgMEOp8S1XFR

      至關於表單設置了enctype屬性,<form enctype="multipart/form-data"></form> 

      若是是GET方法,enctype的值被忽略

 

...
data = new FormData();
data.append('name', 'lyra'); // append方法的第一個參數是控件名;第二個參數是值
data.append('age',18);
xhr.send(data); 
// 還能夠從網頁中獲取表單的值
<form id="form">
   <input type='text' name="age" value="" /><!--必須有name,用於做爲參數名-->
</form>
const formData = document.querySelector('#form'); // formData是整個form表單對應的元素 data = new FormData(formData); data.append('name','lyra'); xhr.send(data); // 還能夠上傳文件等二進制文件 <body> <button onclick="loadJSONDoc()">Get JSON</button> <form id="form"> <label for="image">上傳圖片</label> <input id="file" type='file' name="image" accept="image/png,image/jpeg" multiple /> </form> <script> var xhr; function loadJSONDoc() { xhr = null; if (window.XMLHttpRequest) {// code for Firefox, Chrome, etc. xhr = new XMLHttpRequest(); console.log(xhr.readyState); // 0 } else if (window.ActiveXObject) {// code for IE xhr = new ActiveXObject("Microsoft.XMLHTTP"); } xhr.onreadystatechange = function() { // 0,1,2,3,4 console.log(xhr.readyState) } if (xhr != null) { let data = null; const fileDOM = document.querySelector('input'); const formData = new FormData(); const files = fileDOM.files; // 元素的files屬性獲取文件列表 for(let file of files) { formData.append("image", file, file.name); // 第一個參數是控件名稱;第三個參數是文件名 } data = formData; xhr.open("POST", '/postdata', true); // 1 xhr.send(data); } } </script> </body>
  • "application/x-www-form-urlencoded;charset=UTF-8"

     // 表單POST默認提交方式 

     示例:  id=1&username=lyra

const xhr = new XMLHttpRequest();
xhr.open('POST', '/server', true); // 表單提交必須是POST
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// 若是不設置請求頭,則默認爲text/plain;charset=UTF-8,數據看成字符串傳遞
const data = 'id=1&username=lyra';
xhr.send(data);
  • "text/html;charset=UTF-8":  當傳遞的數據爲document類型時,默認是該類型。
data = document;
xhr.send(data);
  • "text/plain; charset=UTF-8": 當傳遞的數據是對象,數字,字符串,布爾值時,默認都是該類型。

    ⚠️//單獨的傳"name=lyra"屬於字符串,不是URLSearchParams對象。

data = {a: 1};
//data = "22";
//data = 22;
//data = true
xhr.send(data);
  • "application/json; charset=UTF-8": 瀏覽器不會默認該類型,須要手動添加。

     傳參數據須要JSON.stringify()處理。

data = JSON.stringify({a: 0});
xhr.setRequestHeader('Content-Type','application/json;charset=UTF-8');
xhr.send(data);
  • "image/jpeg"或者"image/png": 用於FileAPI上傳圖片

const xhr = new XMLRequestHeader();
xhr.open('POST', '/', true);

const fileDOM = document.querySelector('inputFile');
const file= fileDOM.files[0];

xhr.setRequestHeader('Content-Type', file.type); //能夠省略,系統自動添加
xhr.send(file);

3.send([body])

發送http請求。

若是是GET方法,沒有請求體body或者傳null。

POST方法須要傳請求體body。

4.getAllResponseHeaders()

獲取除了Set-Cookie和Set-Cookie2以外的全部響應頭信息。

若是無響應,返回null; 若是網絡錯誤,返回""

而且每一個頭信息單獨佔一行。例如:

Cache-Control: max-age=31536000
Content-Length: 4260
Content-Type: image/png
Date: Sat, 08 Sep 2012 16:53:16 GMT

如上所示,返回的響應頭信息,每一行都是以回車+換行('\r\n')分割,而且該規則全部操做系統都同樣。

name和value之間以冒號+空格隔開": " ,是一個固定的規範。

因此想要獲取一個name,value是對象的形式,能夠以下操做:

xhr.onreadystatechange = function() { // 只有在readyState >=3 的時候,才能獲取正確的結果。
    if (xhr.readyState ===3 || xhr.readyState === 4) {
        const allHeaders = xhr.getAllResponseHeaders();
        // 注意須要清空兩頭的"\r\n",不然會多出"":undefined
        const resultObj = allHeaders.trim().split("\r\n").reduce((memo, next) => {
            let [name, value] = next.split(": ");
            if (memo[name]) { // 若是請求頭重複
                memo[name] = `${memo[name]}; ${value}`;
            } else {
                memo[name] = value;
            }   
            return memo         
        }, {}) 
        console.log(resultObj);
    }
}

5.getResponseHeader(name)

獲取特定名稱的響應頭信息,除了Set-Cookie和Set-Cookie2

若是有多個結果,會返回一個值,經過空格+逗號隔開。

xhr.getResponseHeader('Content-Type');

6.abort() 

終止請求。xhr.abort(); 

調用後,readyState的值變爲4; status爲0

7. XHR相關事件

除了onprogress事件,其餘的均沒有參數;

全部的監聽函數都應該在xhr.send()方法以前定義。

也能夠經過addEventListen('事件名', fn, false)進行監聽事件的調用。

1.onreadystatechange

當readyState狀態改變時觸發;但重置時,即變爲0,不觸發。

2. onloadstart

當掉用send()方法時,觸發該事件;

3. onprogress

監聽函數有個事件對象的參數,該對象有三個屬性;

xhr.onprogress = function(event) {
  // event對象有三個參數event.loaded/event.total/event.lengthComputable
}

loaded: 已經傳輸的數據

total: 總的數據量

lengthComputable:  布爾值; 進度是否能夠計算

4.onload

請求成功的監聽函數;

5. onloadend

請求結束(成功或者失敗)觸發;readystate === 4

load,error,abort事件都會隨後觸發loadend事件;

6.onabort

請求中斷;調用xhr.abort()觸發; 

7.onerror

請求失敗觸發;網絡問題或者請求路由有誤。

8.ontimeout

從onloadstart開始,超過設置的timeout時間觸發;timeout不爲0。

8.xhr的上傳對象

xhr上有個upload屬性,能夠用來描述文件上傳的狀態。

它有除了onreadystatechange以外,xhr有的其餘7個事件。

xhr.upload.onloadstart
xhr.upload.onload
xhr.upload.onerror
xhr.upload.onloadend
xhr.upload.onprogress
xhr.upload.ontimeout
xhr.upload.onabort

經過upload的監聽事件,查看當前文件上傳的進度

示例:

<body>
  <!--
    progress標籤是H5標籤;閉合標籤不能省略;
    value表示進度;max表示最大值;min表示最小值
  -->
  <progress value="0" min="0" max="100"></progress>
  <script>  
    const xhr = new XMLHttpRequest();
    xhr.open('POST', '/server', true);

    const progressBar = document.querySelector('progress');
    xhr.upload.onprogress = function(e) {
      if(e.lengthComputable) {
        const { loaded, total } = e;
        console.log(loaded, total);
        progressBar.value = (loaded/total)*100;
        // 兼容不支持 <progress> 元素的老式瀏覽器
        progressBar.textContent = progressBar.value;
      }
    }
    xhr.send(new Blob(['hello world']), {type: 'text/plain'});
  </script> 
</body>
相關文章
相關標籤/搜索