Ajax的核心---XMLHttpRequest

? 概述

XMLHttpRequest對(duì)象的作用是與服務(wù)器交互叹谁,在不刷新頁(yè)面的情況下請(qǐng)求特定 URL,獲取數(shù)據(jù)煌张。其實(shí)說(shuō)白了,就是用異步的方式退客,向服務(wù)端發(fā)起請(qǐng)求骏融,獲取或提交數(shù)據(jù),如果非要用通俗又粗俗的話說(shuō)萌狂,它就是用來(lái)發(fā)ajax請(qǐng)求的档玻。

  • 其實(shí)XMLHttpRequest對(duì)象也能發(fā)送同步請(qǐng)求,只要把open()方法的第三個(gè)參數(shù)設(shè)置成false茫藏,就行误趴。
    只是由于同步會(huì)對(duì)用戶體驗(yàn)產(chǎn)生負(fù)面影響,瀏覽器都已經(jīng)把在主線程上的同步請(qǐng)求棄用了务傲。所以凉当,只要記住XMLHttpRequest對(duì)象只能發(fā)送異步請(qǐng)求就對(duì)了。

  • XMLHttpRequest的通信流程是由客戶端發(fā)起的售葡。
    如果需要由服務(wù)端發(fā)起向客戶端的推送看杭,可以用server sent events這里是大神的教程)。
    如果需要全雙工的通信天通,WebSocket這里還是大神的教程)可能是更好的選擇泊窘。

?? 運(yùn)轉(zhuǎn)流程

首先,先創(chuàng)建一個(gè)XMLHttpRequest實(shí)例:

const XHR = new XMLHttpRequest();

然后像寒,調(diào)用open()方法:

const XHR = new XMLHttpRequest();

XHR.open('GET', '/apis');

這行代碼會(huì)啟動(dòng)一個(gè)針對(duì)/apis地址(也可以是絕對(duì)路徑)的GET請(qǐng)求烘豹,要說(shuō)明的是:調(diào)用open()方法并不會(huì)真正發(fā)送請(qǐng)求,而只是啟動(dòng)一個(gè)請(qǐng)求以備發(fā)送诺祸。

要發(fā)送真正的請(qǐng)求携悯,必須像下面這樣調(diào)用send()方法:

const XHR = new XMLHttpRequest();

XHR.open('GET', '/apis');
XHR.send(null);

這里的send()方法接收一個(gè)參數(shù),既要作為請(qǐng)求主體發(fā)送的數(shù)據(jù)筷笨。如果不需要通過(guò)請(qǐng)求主體發(fā)送數(shù)據(jù)憔鬼,則必須傳入null龟劲,因?yàn)檫@個(gè)參數(shù)對(duì)有些瀏覽器來(lái)說(shuō)是必需的。

因?yàn)槲覀儼l(fā)送的是異步請(qǐng)求轴或,所以要依據(jù)readyState屬性的變化昌跌,來(lái)了解異步的過(guò)程。該屬性表示請(qǐng)求/響應(yīng)過(guò)程的當(dāng)前活動(dòng)階段照雁,如圖所示:

readyState屬性的變化.png

只要readyState屬性的值由一個(gè)值變成另一個(gè)值蚕愤,都會(huì)觸發(fā)一次readystatechange事件。通常饺蚊,我們只對(duì)readyState值為4的階段感興趣萍诱,因?yàn)檫@時(shí)所有數(shù)據(jù)都已經(jīng)就緒。不過(guò)污呼,必須在調(diào)用open()之前指定onreadystatechange事件處理程序才能確痹7唬跨瀏覽器兼容性。

const XHR = new XMLHttpRequest();

XHR.onreadystatechange = () => {
    if (XHR.readyState === 4) {
        // blah blah blah...
    } else { }
}

XHR.open('GET', '/apis');
XHR.send(null);

服務(wù)端接收到請(qǐng)求信息燕酷,并做出響應(yīng)(如果通信正常)籍凝,客戶端在收到響應(yīng)后,響應(yīng)的數(shù)據(jù)會(huì)自動(dòng)填充XHR對(duì)象的屬性悟狱,比如:status(響應(yīng)的HTTP狀態(tài))静浴、responseText(作為響應(yīng)主體被返回的文本)......。

一般來(lái)說(shuō)挤渐,可以將HTTP狀態(tài)碼為200作為成功的標(biāo)志苹享。此外,狀態(tài)代碼為304表示請(qǐng)求的資源并沒(méi)有被修改浴麻,可以直接使用瀏覽器中緩存的版本得问,當(dāng)然,也意味著響應(yīng)是有效的软免。

const XHR = new XMLHttpRequest();

XHR.onreadystatechange = () => {
    if (XHR.readyState === 4) {
        if (XHR.status < 300 || XHR.status === 304) {
            // blah blah blah...
        } else {
            alert(`請(qǐng)求失敗了:${XHR.status}`);
        }
    } else { }
}

XHR.open('GET', '/apis');
XHR.send(null);

此處要說(shuō)明一下:狀態(tài)碼為304的意思是說(shuō)瀏覽器可以用緩存的版本宫纬,但是不代表瀏覽器沒(méi)有向服務(wù)端發(fā)起請(qǐng)求。其實(shí)膏萧,瀏覽器是真真正正地向服務(wù)端發(fā)了請(qǐng)求的漓骚,只是服務(wù)端驗(yàn)證了資源的過(guò)期時(shí)間后,僅僅是告訴瀏覽器:“我就不給你數(shù)據(jù)了榛泛,你用你自己那份資源吧蝌蹂,還在保質(zhì)期呢〔芟牵”

另外孤个,在接收到響應(yīng)之前還可以調(diào)用abort()方法,來(lái)取消異步請(qǐng)求沛简。在終止請(qǐng)求后齐鲤,由于內(nèi)存原因斥废,還應(yīng)該對(duì)XHR對(duì)象進(jìn)行解引用操作。

上面给郊,是使用XMLHttpRequest的最簡(jiǎn)單流程牡肉。

下面,再補(bǔ)充一些細(xì)節(jié)操作:

  • 添加/獲取頭部信息

每個(gè)HTTP請(qǐng)求和響應(yīng)都會(huì)帶有相應(yīng)的頭部信息(有的有用丑罪,有的對(duì)開(kāi)發(fā)人員沒(méi)用)荚板,XMLHttpRequest對(duì)象也提供了操作這兩種頭部(即請(qǐng)求頭部和響應(yīng)頭部)信息的方法凤壁。

XMLHttpRequestsetRequestHeader()方法可以設(shè)置自定義的請(qǐng)求頭部信息吩屹,要成功發(fā)送請(qǐng)求頭部信息,此方法必須在調(diào)用open()方法之后且調(diào)用send()方法之前調(diào)用拧抖。

調(diào)用XMLHttpRequestgetResponseHeader()方法并傳入頭部字段名稱煤搜,可以取得相應(yīng)的響應(yīng)頭部信息。而調(diào)用getAllResponseHeaders()方法則可以取得一個(gè)包含所用頭部信息的長(zhǎng)字符串唧席。這兩個(gè)方法也要在調(diào)用open()方法之后且調(diào)用send()方法之前調(diào)用擦盾。

const XHR = new XMLHttpRequest();

XHR.onreadystatechange = () => {
    if (XHR.readyState === 4) {
        if (XHR.status < 300 || XHR.status === 304) {
            // blah blah blah...
        } else {
            alert(`請(qǐng)求失敗了:${XHR.status}`);
        }
    } else { }
}

XHR.open('GET', '/apis');

XHR.setRequestHeader('請(qǐng)求頭的自定義字段名稱', '頭部字段的值');
XHR.getResponseHeader('響應(yīng)頭的字段名稱');
XHR.getAllResponseHeaders();

XHR.send(null);

為什么要添加/獲取頭信息呢?

此之目的僅僅是為了方便信息的傳輸淌哟。服務(wù)器在接收到自定義的頭部信息后迹卢,可以執(zhí)行相應(yīng)的后續(xù)操作;同樣的徒仓,在服務(wù)端腐碱,也可以利用頭部信息向?yàn)g覽器發(fā)送額外的、結(jié)構(gòu)化的數(shù)據(jù)掉弛。而且症见,不是每次請(qǐng)求都必須做這種操作,要看使用場(chǎng)景殃饿,需要用的時(shí)候再用谋作。

  • 監(jiān)測(cè)進(jìn)度

XMLHttpRequest提供了各種在請(qǐng)求被處理期間發(fā)生的事件以供監(jiān)聽(tīng)。這包括定期進(jìn)度通知乎芳、 錯(cuò)誤通知遵蚜,等等。

const XHR = new XMLHttpRequest();

/**
 * 監(jiān)聽(tīng)請(qǐng)求過(guò)程中的各種事件
 */

XHR.onprogress = (oEvent) => {
    // 服務(wù)端到客戶端的傳輸進(jìn)程(下載)
    // 此時(shí)readyState=2

    if (oEvent.lengthComputable) {
        const percentComplete = oEvent.loaded / oEvent.total * 100;

        // blah blah blah...
    } else {
    // 總大小未知時(shí)不能計(jì)算進(jìn)程信息
    }
};

XHR.onload = (oEvent) => {
    // 傳輸完成奈惑,所有數(shù)據(jù)保存在response中吭净。
    // 此時(shí)readyState=4

    // blah blah blah...
};

XHR.onerror = (oEvent) => {
    // 當(dāng)request遭遇錯(cuò)誤時(shí)觸發(fā)。

    // blah blah blah...
};

XHR.onabort= (oEvent) => {
    // 當(dāng)request被停止時(shí)觸發(fā)携取。

    // blah blah blah...
};

XHR.onreadystatechange = () => {
    if (XHR.readyState === 4) {
        if (XHR.status < 300 || XHR.status === 304) {
            // blah blah blah...
        } else {
            alert(`請(qǐng)求失敗了:${XHR.status}`);
        }
    } else { }
}

XHR.open('GET', '/a.txt');
XHR.send(null);

你需要在請(qǐng)求調(diào)用open()之前添加事件監(jiān)聽(tīng)攒钳。否則progress事件將不會(huì)被觸發(fā)。

progress事件同時(shí)存在于下載和上傳的傳輸雷滋。上面的例子是針對(duì)下載的相關(guān)事件不撑。上傳相關(guān)事件在 XMLHttpRequest.upload對(duì)象上被觸發(fā)文兢,像下面這樣:

var oReq = new XMLHttpRequest();

oReq.upload.addEventListener("progress", updateProgress);
oReq.upload.addEventListener("load" , transferComplete);
oReq.upload.addEventListener("error", transferFailed  );
oReq.upload.addEventListener("abort", transferCanceled);

oReq.open();

?? 示例

  • ?? 發(fā)送一個(gè)GET請(qǐng)求
const XHR = new XMLHttpRequest();

XHR.onreadystatechange = () => {
    if (XHR.readyState === 4) {
        if (XHR.status < 300 || XHR.status === 304) {
            console.dir(XHR.response);
        } else { }
    } else { }
}

XHR.open('GET', '/apis');
XHR.send(null);

結(jié)果如圖所示:


沒(méi)有參數(shù)的GET請(qǐng)求.png

給請(qǐng)求加點(diǎn)參數(shù):

XHR.open("GET", "/apis?ab=13&page=1");
XHR.setRequestHeader('CC', 989);
XHR.send(null);

結(jié)果如圖所示:


有參數(shù)的GET請(qǐng)求.png
  • ?? 發(fā)送一個(gè)POST請(qǐng)求
const XHR = new XMLHttpRequest();

XHR.onreadystatechange = () => {
    if (XHR.readyState === 4) {
        if (XHR.status < 300 || XHR.status === 304) {
            console.dir(XHR.response);
        } else { }
    } else { }
}

XHR.open('POST', '/apis');
XHR.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
XHR.send('name=張三豐&職務(wù)=掌門');

結(jié)果如圖所示:


POST請(qǐng)求.png

為什么要添加Content-Type請(qǐng)求頭信息呢?

需要設(shè)置Content-Type頭信息焕檬,完全是由于服務(wù)端的原因姆坚。因?yàn)闊o(wú)論是表單、Ajax实愚、jsonp兼呵,到了服務(wù)端那兒,它只認(rèn)一種請(qǐng)求腊敲,就是form表單請(qǐng)求击喂,所以Ajax發(fā)送的POST請(qǐng)求,服務(wù)端會(huì)認(rèn)成是form表單的POST請(qǐng)求碰辅,而form表單發(fā)送POST請(qǐng)求時(shí)懂昂,如果不設(shè)置enctype屬性,就會(huì)以默認(rèn)的application/x-www-form-urlencoded方式提交表單没宾,因此不加不行啊凌彬。

如果要上傳文件,需要將Content-Type頭的值設(shè)置為multipart/form-data循衰,總之铲敛,跟form表單的提交方式保持一致就對(duì)了。

這篇《Express.js 解析 Post 數(shù)據(jù)類型的正確姿勢(shì)》介紹了POST請(qǐng)求的四種方式会钝,值得一看??伐蒋。

好了,先寫(xiě)到這兒吧顽素。

--(完)--

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末咽弦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子胁出,更是在濱河造成了極大的恐慌型型,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件全蝶,死亡現(xiàn)場(chǎng)離奇詭異闹蒜,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)抑淫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門绷落,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人始苇,你說(shuō)我怎么就攤上這事砌烁。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵函喉,是天一觀的道長(zhǎng)避归。 經(jīng)常有香客問(wèn)我,道長(zhǎng)管呵,這世上最難降的妖魔是什么梳毙? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮捐下,結(jié)果婚禮上账锹,老公的妹妹穿的比我還像新娘。我一直安慰自己坷襟,他們只是感情好奸柬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著啤握,像睡著了一般鸟缕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上排抬,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音授段,去河邊找鬼蹲蒲。 笑死,一個(gè)胖子當(dāng)著我的面吹牛侵贵,可吹牛的內(nèi)容都是我干的届搁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼窍育,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼卡睦!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起漱抓,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤表锻,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后乞娄,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體瞬逊,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年仪或,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了确镊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡范删,死狀恐怖蕾域,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情到旦,我是刑警寧澤旨巷,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布廓块,位于F島的核電站,受9級(jí)特大地震影響契沫,放射性物質(zhì)發(fā)生泄漏带猴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一懈万、第九天 我趴在偏房一處隱蔽的房頂上張望拴清。 院中可真熱鬧,春花似錦会通、人聲如沸口予。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沪停。三九已至,卻和暖如春裳涛,著一層夾襖步出監(jiān)牢的瞬間木张,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工端三, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留舷礼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓郊闯,卻偏偏與公主長(zhǎng)得像妻献,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子团赁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355