詳談ajax(原理、構(gòu)造、封裝......)

說起ajax,大家都不陌生。但是由于目前很多框架或者庫(kù)等都對(duì)網(wǎng)絡(luò)請(qǐng)求做了封裝,導(dǎo)致了很多初學(xué)者只知其然而不知其所以然。所以今天我們就詳細(xì)了解一下ajax的實(shí)現(xiàn)原理和封裝ajax的關(guān)鍵步驟。

ajax的核心是XMLHttpRequest對(duì)象。首先我們先創(chuàng)建一個(gè)XMLHTTPRequest對(duì)象var xhr = new XMLHttpRequest();。

注意:本文所提及的內(nèi)容不兼容古老的IE,有想了解的同學(xué)自行查閱ActiveXObject相關(guān)內(nèi)容。


XMLHttpRequest

在使用XMLHttpRequest對(duì)象的第一步,我們首先要調(diào)用open方法來(lái)初始化請(qǐng)求參數(shù),xhr.open('get','/test',true),雖然名字叫open,但是此時(shí)請(qǐng)求還并沒有發(fā)送。

open(method, url[, async, username, password])

  • method:請(qǐng)求類型,例如GET,POST等
  • url:請(qǐng)求地址(這里有同源限制,就是我們經(jīng)常會(huì)看到的跨域問題啦)
  • async:是否發(fā)送異步請(qǐng)求。可選參數(shù),默認(rèn)為true。
  • username&password:可選參數(shù),授權(quán)驗(yàn)證使用的,但是我們一般不這么用,使用后請(qǐng)求變成這個(gè)樣子了,http(s)://username:password@url。

如果調(diào)用了open方法后再次對(duì)它進(jìn)行調(diào)用,則相當(dāng)于調(diào)用了abort方法,abort方法我們?cè)诤竺娼榻B。

如果我們想為為請(qǐng)求綁定一些操作,這個(gè)時(shí)候就可以開始啦。常用的操作有如下幾個(gè):

setRequestHeader(key, value)

顧名思義,這個(gè)方法用于設(shè)置請(qǐng)求頭內(nèi)容。

  • key:要設(shè)置的請(qǐng)求頭名稱
  • value:對(duì)應(yīng)請(qǐng)求頭的值

overrideMimeType(type)

重寫服務(wù)器返回的MIME類型。通過這個(gè)方法可以告訴服務(wù)器你想要的數(shù)據(jù)類型。

注意:以上這些操作必須定義在send方法之前。否則,就拿setRequestHeader來(lái)說,你都把請(qǐng)求發(fā)出去了再設(shè)置還有什么用?

這個(gè)時(shí)候,我們就可以通過調(diào)用send 方法來(lái)發(fā)送請(qǐng)求了,xhr.send(null)。

send(data)

發(fā)送請(qǐng)求,如果是同步請(qǐng)求的話,會(huì)阻塞代碼的執(zhí)行,直至收到服務(wù)器響應(yīng)才會(huì)繼續(xù)。

  • data:發(fā)送給服務(wù)器的數(shù)據(jù)。為了兼容不同的瀏覽器,即使是不需要傳數(shù)據(jù),也需要傳入?yún)?shù)null。

readyStateChanhe()

每次readyState的值改變的時(shí)候都會(huì)觸發(fā)這個(gè)函數(shù)。

getResponseHeader(name)

獲取指定響應(yīng)頭部的值,參數(shù)是響應(yīng)頭部的名稱,并且不區(qū)分大小寫。

getAllResponseHeaders()

獲取服務(wù)器發(fā)送的所有HTTP響應(yīng)的頭部。

在這里我們穿插幾個(gè)概念,readyState,這個(gè)屬性表明了請(qǐng)求的狀態(tài),伴隨HTTP請(qǐng)求的整個(gè)生命周期,它的值表明此時(shí)請(qǐng)求所處的階段,具體如下:

readyState

數(shù)值 描述
0 初始化,open()尚未調(diào)用
1 open()已經(jīng)調(diào)用,但是send未調(diào)用
2 已獲取到返回頭信息
3 正在下載返回體信息
4 請(qǐng)求完成

還有幾個(gè)較為常用的屬性

名稱 含義
responseText 響應(yīng)的文本
status 響應(yīng)的狀態(tài)碼
statusText 響應(yīng)的狀態(tài)信息
responseXML 響應(yīng)內(nèi)容是“text/xml”或者是“application/xml”格式的時(shí)候,這個(gè)屬性的值就是響應(yīng)數(shù)據(jù)的XMLDOM文檔。

我們用下面這段代碼做個(gè)測(cè)試

var xhr = new XMLHttpRequest();
console.log(xhr.readyState)
xhr.onreadystatechange = function(){
    console.log('------')
    console.log('readyState:' + xhr.readyState)
    console.log('ResponseHeaders:' + xhr.getAllResponseHeaders())
    console.log('ResponseText:' + xhr.responseText.length)
    console.log('------')
}
xhr.open('get','/')
xhr.send(null)

下圖我們可以直觀的看到在創(chuàng)建了XMLHttpRequest對(duì)象的時(shí)候,readyState的值為0。

1.webp.jpg

然后我們定義了onreadystatechange函數(shù),讓其打印一些屬性,并調(diào)用open方法,此時(shí)readyState變?yōu)?。

2.webp.jpg

最后我們調(diào)用send方法,可以看到經(jīng)歷了如下過程:

  1. send方法調(diào)用之后,readyState變?yōu)?,此時(shí)responseHeader已經(jīng)獲取到了,responseText為空;
  2. 響應(yīng)數(shù)據(jù)開始下載,readyState變?yōu)?
  3. 響應(yīng)數(shù)據(jù)下載結(jié)束,readyState變?yōu)?.我們可以發(fā)現(xiàn)此時(shí)responseText的長(zhǎng)度比之前長(zhǎng)。
3.webp.jpg

abort()

取消響應(yīng),調(diào)用這個(gè)方法會(huì)終止已發(fā)送的請(qǐng)求。我們嘗試在之前的代碼最后加一句。

xhr.abort();
console.log(xhr.readyState);

4.webp.jpg
5.webp.jpg

也就是說,send執(zhí)行以后,并沒有去嘗試請(qǐng)求數(shù)據(jù),而是直接取消掉了,并且我們發(fā)現(xiàn)abort會(huì)將readyState的值置為0。

除此之外,XMLHttpRequest還有一個(gè)很重要的屬性withCredentials,cookie在同域請(qǐng)求的時(shí)候,會(huì)被自動(dòng)攜帶在請(qǐng)求頭中,但是跨域請(qǐng)求則不會(huì),除非把withCredentials的值設(shè)為true(默認(rèn)為false)。同時(shí)需要在服務(wù)端的響應(yīng)頭部中設(shè)置Access-Control-Allow-Credentials:true。不僅如此Access-Control-Allow-Origin的值也必須為當(dāng)前頁(yè)面的域名。


封裝

到此為止,我們終于講完了XMLHttpRequest的一些常用概念。接下來(lái),我們嘗試自己封裝一個(gè)支持get和post的簡(jiǎn)易jax請(qǐng)求。

function ajax(url, option){
    option = option || {};
    var method = (option.method || 'GET').toUpperCase(),
        async = option.async === undefined ? true : option.async,
        params = handleParams(option.data);
    var xhr = new XMLHttpRequest();
    if(async){
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4){
               callback(option,xhr);
            }
        };
    }
    if (method === 'GET'){
        xhr.open("GET",url + '?' + params, async);
        xhr.send(null)
    }else if (method === 'POST'){
        xhr.open('POST', url, async);
        xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
        xhr.send(params);
    }
    if(!async){
        callback(option,xhr);
    }
    function callback(opt,obj){
        var status = obj.status;
        if (status >= 200 && status < 300 ){
            opt.success && opt.success(obj.responseText,obj.responseXML);
        }else{
            opt.fail && opt.fail(status);
        }
    }
    function handleParams(data) {  
        var arr = [];
        for (var i in data) {
            arr.push(encodeURIComponent(i) + '=' + encodeURIComponent(data[i]));
        }
        return arr.join('&');
    }
}
//  測(cè)試調(diào)用
ajax('/xxx',{
    method:'POST',
    data:{
        key: 'test'
    },
    success:function(){
        console.log('success')
    },
    fail:function(){
        console.log('fail')
    }
});

小結(jié)

其實(shí)ajax實(shí)現(xiàn)原理并不復(fù)雜,復(fù)雜的是容錯(cuò)、兼容性等的處理,使用的時(shí)候盡量使用庫(kù)或者框架提供的封裝,避免不必要的漏洞。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ??2005 年,Jesse James Garrett 發(fā)表了一篇在線文章,題為“Ajax: A new App...
    霜天曉閱讀 942評(píng)論 0 1
  • 本文詳細(xì)介紹了 XMLHttpRequest 相關(guān)知識(shí),涉及內(nèi)容: AJAX、XMLHTTP、XMLHttpReq...
    semlinker閱讀 13,994評(píng)論 2 18
  • 寫在前面 本篇主要內(nèi)容是結(jié)合“XMLHttpRequest Level 1”規(guī)范和w3school網(wǎng)站中針對(duì)XML...
    JSON_NULL閱讀 1,632評(píng)論 3 9
  • 瀏覽器與服務(wù)器之間,采用HTTP協(xié)議通信。用戶在瀏覽器地址欄鍵入一個(gè)網(wǎng)址,或者通過網(wǎng)頁(yè)表單向服務(wù)器提交內(nèi)容,這時(shí)瀏...
    許先生__閱讀 551評(píng)論 1 2
  • AJAX 原生js操作ajax 1.創(chuàng)建XMLHttpRequest對(duì)象 var xhr = new XMLHtt...
    碧玉含香閱讀 3,557評(píng)論 0 7

友情鏈接更多精彩內(nèi)容