摘要:所以今天我們就詳細(xì)了解一下的實(shí)現(xiàn)原理和封裝的關(guān)鍵步驟。接下來,我們嘗試自己封裝一個(gè)支持和的簡(jiǎn)易請(qǐng)求。測(cè)試調(diào)用小結(jié)其實(shí)實(shí)現(xiàn)原理并不復(fù)雜,復(fù)雜的是容錯(cuò)兼容性等的處理,使用的時(shí)候盡量使用庫或者框架提供的封裝,避免不必要的漏洞。
說起ajax,大家都不陌生。但是由于目前很多框架或者庫等都對(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方法來初始化請(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)求??蛇x參數(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來說,你都把請(qǐng)求發(fā)出去了再設(shè)置還有什么用?
這個(gè)時(shí)候,我們就可以通過調(diào)用send 方法來發(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。
然后我們定義了onreadystatechange函數(shù),讓其打印一些屬性,并調(diào)用open方法,此時(shí)readyState變?yōu)?。
最后我們調(diào)用send方法,可以看到經(jīng)歷了如下過程:
send方法調(diào)用之后,readyState變?yōu)?,此時(shí)responseHeader已經(jīng)獲取到了,responseText為空;
響應(yīng)數(shù)據(jù)開始下載,readyState變?yōu)?
響應(yīng)數(shù)據(jù)下載結(jié)束,readyState變?yōu)?.我們可以發(fā)現(xiàn)此時(shí)responseText的長(zhǎng)度比之前長(zhǎng)。
abort()
取消響應(yīng),調(diào)用這個(gè)方法會(huì)終止已發(fā)送的請(qǐng)求。我們嘗試在之前的代碼最后加一句。
xhr.abort(); console.log(xhr.readyState);
也就是說,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)前頁面的域名。
封裝到此為止,我們終于講完了XMLHttpRequest的一些常用概念。接下來,我們嘗試自己封裝一個(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í)候盡量使用庫或者框架提供的封裝,避免不必要的漏洞。
補(bǔ)充感謝@蝦嗶嗶的提問,這里做個(gè)簡(jiǎn)單的補(bǔ)充說明。
async是一個(gè)可選的布爾值參數(shù),默認(rèn)為true,意味著是否執(zhí)行異步操作,如果值為false,則send()方法不會(huì)返回任何東西,直到接受到了服務(wù)器的返回?cái)?shù)據(jù)。如果為值為true,一個(gè)對(duì)開發(fā)者透明的通知會(huì)發(fā)送到相關(guān)的事件監(jiān)聽者。這個(gè)值必須是true,如果multipart 屬性是true,否則將會(huì)出現(xiàn)一個(gè)意外。
根據(jù)我的分析,當(dāng)async為false的時(shí)候,readyState會(huì)在send方法之后直接由1變成4。也就是說異步模式,send方法會(huì)立刻返回。同步模式下,只有響應(yīng)完全接受后,send才會(huì)返回。
另外,由于同步模式會(huì)阻塞,較新版本的Chrome在主線程上的同步請(qǐng)求已被棄用。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/95877.html
摘要:開公眾號(hào)差不多兩年了,有不少原創(chuàng)教程,當(dāng)原創(chuàng)越來越多時(shí),大家搜索起來就很不方便,因此做了一個(gè)索引幫助大家快速找到需要的文章系列處理登錄請(qǐng)求前后端分離一使用完美處理權(quán)限問題前后端分離二使用完美處理權(quán)限問題前后端分離三中密碼加鹽與中異常統(tǒng)一處理 開公眾號(hào)差不多兩年了,有不少原創(chuàng)教程,當(dāng)原創(chuàng)越來越多時(shí),大家搜索起來就很不方便,因此做了一個(gè)索引幫助大家快速找到需要的文章! Spring Boo...
摘要:大家中秋假期快樂,假期分享一些原理設(shè)計(jì)文章給大家原創(chuàng)不易,歡迎轉(zhuǎn)發(fā),一起學(xué)習(xí)凌晨寫的,不容易哈,收藏或者點(diǎn)個(gè)贊吧在常見的單頁應(yīng)用中,我們都會(huì)有一個(gè)根文件,里面放置一個(gè)然后配置路由來切換很多人在子父組件嵌套關(guān)系下的生命周期鉤子函數(shù)如何應(yīng)用, 大家中秋假期快樂,假期分享一些原理設(shè)計(jì)文章給大家 原創(chuàng)不易,歡迎轉(zhuǎn)發(fā),一起學(xué)習(xí)(凌晨寫的,不容易哈,收藏或者點(diǎn)個(gè)贊吧) 在常見的單頁應(yīng)用中,我們都...
摘要:下面我會(huì)詳細(xì)地從源碼的角度分析下文簡(jiǎn)寫成是如何實(shí)現(xiàn)自動(dòng)注入的原理。文件解析器,解析對(duì)應(yīng)的文件信息,并將文件信息注冊(cè)到中。節(jié)點(diǎn)解析器,用于構(gòu)建節(jié)點(diǎn)信息。注冊(cè)與綁定類,將的類信息與綁定。 微信公眾號(hào)「后端進(jìn)階」,專注后端技術(shù)分享:Java、Golang、WEB框架、分布式中間件、服務(wù)治理等等。 老司機(jī)傾囊相授,帶你一路進(jìn)階,來不及解釋了快上車! mybatis-plus是完全基于myba...
閱讀 3104·2021-11-22 09:34
閱讀 607·2021-11-22 09:34
閱讀 2457·2021-10-08 10:18
閱讀 3390·2021-09-22 15:57
閱讀 2603·2021-09-22 15:25
閱讀 2423·2019-08-30 15:54
閱讀 2139·2019-08-30 15:44
閱讀 1811·2019-08-29 11:18