推薦閱讀地址
掘金
求 start
大家好,我是林一一,下面這篇文章是關(guān)于 Ajax,fetch,axios的超高頻面試題,一起來閱讀吧??
思維導(dǎo)圖
一、Ajax
1. 概念
Ajax全稱:async javaScript and xml。xml:是一種可以擴(kuò)展的文本標(biāo)記語言,可以擴(kuò)展自定義的語義標(biāo)簽。很早以前 xml 常用于從服務(wù)端返回?cái)?shù)據(jù)結(jié)構(gòu),現(xiàn)在基本都是使用 json 格式返回?cái)?shù)據(jù)。
2. 作用
在不刷新全局的條件下,局部刷新頁面。
3. 四步創(chuàng)建 Ajax
- 創(chuàng)建
Ajax實(shí)例,let xhr = new XMLHttpRequest(),IE6 不兼容這種寫法 - 打開請(qǐng)求,配置請(qǐng)求前的配置項(xiàng),共 5個(gè)參數(shù),
xhr.open([http method], [url], [async], [userName], [userPass])http methods有常用的請(qǐng)求方式有,post, get, delete, put, head, options, trace, connect。async代表異步,默認(rèn)是true異步,false是同步。[url]:是想服務(wù)器請(qǐng)求的api。[userName], [userPass],代表用戶名和密碼
- http methods 細(xì)分:delete:刪除服務(wù)器端的某些數(shù)據(jù),一般是文件。put:向服務(wù)器上存放某些內(nèi)容,一般是文件,head:只是獲取從服務(wù)器端返回的請(qǐng)求頭信息,不要響應(yīng)主體的內(nèi)容。options:一般用于向服務(wù)器發(fā)送探測(cè)性請(qǐng)求,看是否連接成功
- 事件監(jiān)聽
readystatechange,一般監(jiān)聽 ajax 狀態(tài)碼發(fā)生改變的事件,這個(gè)事件可以獲取服務(wù)器返回的響應(yīng)主和請(qǐng)求頭。xhr.onreadystatechange = function (){},對(duì)于同步執(zhí)行的 Ajax 請(qǐng)求代碼步驟三要放在send的前面。否則沒有意義。 - 發(fā)送 ajax 請(qǐng)求,ajax 任務(wù)開始執(zhí)行。
xhr.send([]),XMLHttpRequest.send()方法中如果 Ajax 請(qǐng)求是異步的則這個(gè)方法發(fā)送請(qǐng)求后就會(huì)返回,如果Ajax請(qǐng)求是同步的,那么請(qǐng)求必須知道響應(yīng)后才會(huì)返回。
第五步算上的話,就是讀取返回的數(shù)據(jù)
xhr.responseText。
// 1. 創(chuàng)建 XMLHttpRequest 實(shí)例
let xhr = XMLHttpRequest()
// 2. 打開和服務(wù)器的連接
xhr.open('get', 'URL')
// 3.發(fā)送
xhr.send()
// 4. 接收變化。
xhr.onreadystatechange = () => {
if(xhr.readyState == 4 && xhr.status == 200){ // readyState: ajax 狀態(tài),status:http 請(qǐng)求狀態(tài)
console.log(xhr.responseText); //響應(yīng)主體
}
}
4. Ajax 狀態(tài)和 HTTP 狀態(tài)碼
- Ajax 狀態(tài)一共有 5 種
xhr.readyState,分別是0, 1, 2, 3, 4
- 狀態(tài) 0:
unsent,剛創(chuàng)建的XMLHttpRequest實(shí)例,還沒有發(fā)送。- 狀態(tài) 1:(載入)已調(diào)用
send()方法,正在發(fā)送請(qǐng)求。- 狀態(tài) 2:(載入完成)
send()方法執(zhí)行完成,已經(jīng)接收到全部響應(yīng)內(nèi)容- 狀態(tài) 3:
loading,(交互)正在解析響應(yīng)內(nèi)容- 狀態(tài) 4:
done,表示響應(yīng)的主體內(nèi)容解析完成,可以在客戶端調(diào)用了
- HTTP 常見的狀態(tài)碼。
2xx:表示請(qǐng)求已經(jīng)被服務(wù)器接收,理解,請(qǐng)接受。常見的有,200表示ok,表示服務(wù)能夠返回信息。204No Content 無內(nèi)容。服務(wù)器成功處理,但未返回內(nèi)容。3xx:一類重要的高頻考點(diǎn),301:表示永久轉(zhuǎn)移,返回舊域名會(huì)跳轉(zhuǎn)到心域名。302:臨時(shí)轉(zhuǎn)移。一般用于服務(wù)器負(fù)載均衡,但服務(wù)器的并發(fā)數(shù)達(dá)到最大時(shí),服務(wù)器會(huì)將后續(xù)訪問的用戶轉(zhuǎn)移到其他服務(wù)器上去。307:表示臨時(shí)重定向。304:表示不設(shè)置緩存,對(duì)于不經(jīng)常更新的文件,例如css/js/html文件,服務(wù)器會(huì)結(jié)合客戶端設(shè)置304狀態(tài),加載過的資源下次請(qǐng)求時(shí)會(huì)在客戶端中獲取。4xx:表示語義有誤,請(qǐng)求無法被服務(wù)器端理解。400:表示請(qǐng)求的參數(shù)錯(cuò)誤。401:表示無權(quán)限訪問。404:表示請(qǐng)求的資源不存在。413:表示和服務(wù)器的交互過大。5xx:服務(wù)器端出錯(cuò)。500:表示服務(wù)器端出現(xiàn)未知的錯(cuò)誤。503:服務(wù)器超負(fù)荷。
5. ajax 中常用的屬性和方法
onabort: 表示請(qǐng)求中斷后要處理的事。和xhr.abort()一起使用。ontimeout: 表示請(qǐng)求的超時(shí),執(zhí)行的方法。和timeout設(shè)定的事件一起使用。response: 響應(yīng)的主體內(nèi)容。responseText: 響應(yīng)的具體內(nèi)容是字符串,一般是 json 字符串responseXML: 響應(yīng)的具體內(nèi)容是文檔。status: http 的狀態(tài)碼。statusText: 狀態(tài)碼描述withCredentials:表示是否允許跨域。getAllResponseHeaders:獲取所有響應(yīng)頭信息。xhr.open():打開URL請(qǐng)求。xhr.send():表示發(fā)送 ajax。setRequestHeader(): 設(shè)置請(qǐng)求頭。這個(gè)屬性在必須在xhr.open()后面。
思考
-
post和get有什么區(qū)別。
http的所有請(qǐng)求方法中都可以從服務(wù)端獲取數(shù)據(jù),和傳遞內(nèi)容。get:主要是從服務(wù)器獲取數(shù)據(jù)。post主要發(fā)送數(shù)據(jù)給服務(wù)器。
GET和POST本質(zhì)上就是TCP鏈接,并無差別,但是由于HTTP的規(guī)定和瀏覽器/服務(wù)器的限制具體由如下的區(qū)別
- 從
緩存的角度上說,get請(qǐng)求會(huì)被瀏覽器默認(rèn)緩存下來,而post請(qǐng)求默認(rèn)不會(huì)。 - 從
參數(shù)來說,get請(qǐng)求的參數(shù)一般放在url中,post請(qǐng)求是放在請(qǐng)求主體中,因此post請(qǐng)求更安全一些。 - 從
TCP上來說,GET產(chǎn)生一個(gè)TCP數(shù)據(jù)包;POST產(chǎn)生兩個(gè)TCP數(shù)據(jù)包。對(duì)于GET方式的請(qǐng)求,瀏覽器會(huì)把http header和data一并發(fā)送出去,服務(wù)器響應(yīng)200(返回?cái)?shù)據(jù));而對(duì)于POST,瀏覽器先發(fā)送header,服務(wù)器響應(yīng)100 continue,瀏覽器再發(fā)送data,服務(wù)器響應(yīng)200 ok(返回?cái)?shù)據(jù))。雖然 post 請(qǐng)求需要發(fā)送兩次,但是時(shí)間上是基本差別不大的。還有并不是所有瀏覽器都會(huì)在POST中發(fā)送兩次包,Firefox 就只發(fā)送一次
- 來一道思考題,求輸出結(jié)果
let xhr = new XMLHttpRequest()
xhr.open('post', 'api')
xhr.onreadystatechange = () =>{
if(xhr.readyState == 2){
console.log(2)
}
if(xhr.readyState == 4){
console.log(4)
}
}
xhr.send()
console.log(3)
/* 輸出
* 3 2 4
*/
如果知道任務(wù)隊(duì)列的概念,不難知道輸出的結(jié)果,因?yàn)槭钱惒秸?qǐng)求,所以同步的主任務(wù)先輸出
3,最后輸出2, 4。如果上面xhr.open('post', 'api')變成xhr.open('post', 'api', false)后代碼就是同步的,任務(wù)隊(duì)列中只有主任務(wù)輸出的結(jié)果變成2, 4, 3。
- 再來一道思考題,在同步請(qǐng)求中下面代碼輸出的是什么
let xhr = new XMLHttpRequest()
xhr.open('get', 'xxx', false)
xhr.send()
xhr.onreadystatechange = () => {
console.log(xhr.readyState)
}
上面的結(jié)果什么也沒有輸出,這里涉及到任務(wù)隊(duì)列的知識(shí),
onreadystatechange這個(gè)事件監(jiān)聽的是 ajax 狀態(tài)碼的變化,上面的同步請(qǐng)求中xhr.send()已經(jīng)執(zhí)行完后 ajax 的狀態(tài)碼由 0 變成了 4 還沒有執(zhí)行到onreadystatechange這個(gè)監(jiān)聽事件,所以沒有輸出結(jié)果。如果將監(jiān)聽事件放在xhr.send()之前,那么輸出的就是 4。
二、axios
axios是使用promise封裝的ajax。axios 不是一個(gè)類是一個(gè)方法。
1. axios 屬性
axios 有
get, post, put, patch ,delete等請(qǐng)求方式,get,post返回的實(shí)例都是promise,所以可以使用promise的方法,下面給出基本的實(shí)用方法。
-
axios.get(),向服務(wù)器發(fā)送一個(gè)get請(qǐng)求。
axios.get('apiURL', {
param: {
id: 1
}
}).then(res=>{
console.log(res);
})
.catch( error=>{
console.log(error)
}
param中的的鍵值對(duì)最終會(huì)?的形式,拼接到請(qǐng)求的鏈接上,發(fā)送到服務(wù)器。
-
axios.post()示例
axios.post('apiURL',{
user: '林一一',
age: 18
}).then( res=>{
console.log(res);
})
.catch( error=>{
console.log(error)
}
-
axios.put()示例
axios.put('apiURL', {
name: '林一一',
})
-
axios.patch(url[, data[, config]])示例
axios.patch('apiURL', {
id: 13,
},{
timeout: 1000,
})
-
axios.delete()示例
axios.delete('apiURL', {
params: {
id: 1
},
timeout: 1000
})
2. 一次并發(fā)的請(qǐng)求 axios.all([])
axios.all()可以實(shí)現(xiàn)多個(gè)請(qǐng)求,且請(qǐng)求都需要在完成后才再去做某事。
let requestArr = [axios.get('apiURL/1'), axios.get('apiURL/2'), axios.post('apiURL/3', {id: 3})]
axios.all(requestArr).then(res => {
console.log(res)
})
思考,axios.all() 是怎么實(shí)現(xiàn)并發(fā)請(qǐng)求的?
axios.all()使用的是promise.all()實(shí)現(xiàn)的,來看看 axios 中的源碼
axios.all = function all(promises) {
return Promise.all(promises);
};
3. axios 中的配置項(xiàng)。
實(shí)用的 axios配置項(xiàng)
三、fetch
1. 介紹 fetch
-
fetch:是http的數(shù)據(jù)請(qǐng)求方式,是XMLHttpRequest的一種代替方案,沒有使用到XMLHttpRequest這個(gè)類。fetch不是 ajax,而是原生的 js。fetch()使用Promise,不使用回調(diào)函數(shù)。fetch是 ES8 中新增的 api,兼容性不是很好,IE 完全不兼容fetch寫法。 -
fetch()采用模塊化設(shè)計(jì),API 分散在Response對(duì)象、Request對(duì)象、Headers對(duì)象上。 -
fetch()通過數(shù)據(jù)流(Stream 對(duì)象)處理數(shù)據(jù),對(duì)于請(qǐng)求大文件或者網(wǎng)速慢的場(chǎng)景相當(dāng)有用。XMLHttpRequest沒有使用數(shù)據(jù)流,所有的請(qǐng)求都必須完成后才拿到 - 在默認(rèn)情況下
fetch不會(huì)接受或者發(fā)送cookies
2. fetch(url, optionObj) 基本使用
- 接收第一個(gè)參數(shù)為請(qǐng)求的
url,默認(rèn)的請(qǐng)求方式是get。 - 第二個(gè)是可選參數(shù)
optionObj,可以控制不同配置的屬性,比如method:屬性是字符串。headers: 一個(gè)對(duì)象,可以設(shè)定 http 的請(qǐng)求頭。body:POST請(qǐng)求的數(shù)據(jù)體,屬性也是字符串。credentials表示是否可以攜帶cookie,includes表示是否同源都包含cookie。 -
fetch參數(shù)沒有同步的設(shè)定,因?yàn)?fetch是基于promise封裝的本身就是異步。 -
fetch雖然使用的是promise封裝的,但是catch函數(shù)不能直接的捕獲到錯(cuò)誤,需要在第一個(gè)then函數(shù)內(nèi)做些操作。
fetch發(fā)送 post請(qǐng)求時(shí),當(dāng)發(fā)生的是跨域請(qǐng)求,fetch會(huì)先發(fā)送一個(gè)OPTIONS請(qǐng)求,來確認(rèn)服務(wù)器是否允許接受請(qǐng)求,這個(gè)請(qǐng)求主要是用來詢問服務(wù)器是否允許修改header頭等一些操作。服務(wù)器同意后返回 204,才會(huì)發(fā)送真正的請(qǐng)求。沒有發(fā)生跨域的情況下不會(huì)產(chǎn)生兩次請(qǐng)求。
一個(gè) get 請(qǐng)求
const pro = fetch('https://lyypro.gitee.io/blog/')
pro.then( response =>
response.json()
).catch( err => {
console.log(err)
})
一個(gè) post 請(qǐng)求
const URL = 'https://lyypro.gitee.io/blog/'
const init = {
method: 'POST',
header: {
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8",
},
data: 'id=12&name=林一一',
credentials: 'include'
}
const pro = fetch(URL, init)
pro.then( response =>
response.json()
).catch( err => {
console.log(err)
})
上面的請(qǐng)求都可以使用
await, async來修改這里不展示。同時(shí)是為post請(qǐng)求中,data屬性只支持字符串,我們可以使用
4. fetch 的三個(gè)模塊
-
Response模塊:fetch請(qǐng)求發(fā)送后,會(huì)得到一個(gè)服務(wù)器的響應(yīng)response,這個(gè)響應(yīng)對(duì)于著 http 的回應(yīng)。 -
Request模塊:這是用于請(qǐng)求服務(wù)器的模塊,上面提到的data, header, method都是Request模塊的屬性。 -
Headers,這是一個(gè)在Response.headers上的屬性用于操控響應(yīng)頭的信息。
上面三者的詳細(xì)屬性可以看看 阮老師的 Fetch API 教程
5.思考 發(fā)送 post 2 次請(qǐng)求的原因
使用
fetch發(fā)送post請(qǐng)求時(shí)如果是跨域,那么導(dǎo)致fetch第一次發(fā)送了一個(gè)Options請(qǐng)求,詢問服務(wù)器是否支持修改的請(qǐng)求頭,如果服務(wù)器支持,則在第二次中發(fā)送真正的請(qǐng)求。
6.思考 fetch 的缺點(diǎn)
fetch的get/head請(qǐng)求不能設(shè)置body屬性。fetch請(qǐng)求后,服務(wù)器返回的狀態(tài)碼無論是多少包括(4xx, 5xx),fetch都不認(rèn)為是失敗的,也就是使用catch也不能直接捕捉到錯(cuò)誤,需要再第一個(gè)then中做一些處理。
四、面試題
封裝原生的 Ajax
1.實(shí)現(xiàn)一個(gè) Ajax。
將原生的 ajax 封裝成 promise
var myNewAjax = function (url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('get', url);
xhr.send(data);
xhr.onreadystatechange = function () {
if (xhr.status == 200 && readyState == 4) {
var json = JSON.parse(xhr.responseText);
resolve(json)
} else if (xhr.readyState == 4 && xhr.status != 200) {
reject('error');
}
}
})
}
2. ajax 有幾個(gè)狀態(tài),分別代表什么?
ajax 5個(gè)狀態(tài)向上看。
3. fetch VS ajax VS axios 區(qū)別
- 傳統(tǒng)的
ajax利用的是XMLHttpRequest這個(gè)對(duì)象,和后端進(jìn)行交互。JQuery ajax是對(duì)原生XHR的封裝,多請(qǐng)求間有簽到的話就會(huì)出現(xiàn)回調(diào)地獄的問題。 -
axios使用promise封裝xhr,解決了回調(diào)地獄問題 -
fetch不是XMLHttpRequest,fetch是原生的 js,使用的是promise。
4. Fetch 和 Ajax 比有什么優(yōu)點(diǎn)?
fetch 使用的是 promise 方便使用異步,沒有回調(diào)地獄的問題。
5. 如何實(shí)現(xiàn)一個(gè) ajax 請(qǐng)求?如果我想發(fā)出兩個(gè)有順序的 ajax 需要怎么做?
實(shí)現(xiàn) ajax 的請(qǐng)求就是上面的創(chuàng)建 ajax 的逼格步驟。實(shí)現(xiàn)兩個(gè)有順序的 ajax 可以使用
promise.then()
6. Ajax 怎么解決瀏覽器緩存問題
- 設(shè)置請(qǐng)求頭,在 ajax 發(fā)送請(qǐng)求前加上
anyAjaxObj.setRequestHeader("If-Modified-Since","0")或anyAjaxObj.setRequestHeader("Cache-Control","no-cache")。 - 在 URL 后面加上一個(gè)隨機(jī)數(shù):
"fresh=" + Math.random()。 或在后面加上時(shí)間搓:"nowtTime=" + new Date().getTime()。 - 如果是使用 jQuery,直接這樣就可以了
$.ajaxSetup({cache:false})。這樣頁T面的所有 ajax 都會(huì)執(zhí)行這條語句就是不需要保存緩存記錄。
五、參考
六、結(jié)束
感謝閱讀到這里,如果著篇文章能對(duì)你有一點(diǎn)啟發(fā)或幫助的話歡迎 star, 我是林一一,下次見。