這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

一、axios簡介
axios是什么?
Axios 是一個基于 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中,
axios有什么特性?(不得不說面試被問到幾次)
- 從瀏覽器中創建 XMLHttpRequests
- 從 node.js 創建 http 請求
- 支持 Promise API
- 攔截請求和回應
- 轉換請求資料和回應資料
- 取消請求
- 自動轉換 JSON 資料
- 客戶端支持防御 XSRF
實際上,axios可以用在瀏覽器和 node.js 中是因為,它會自動判斷當前環境是什么,如果是瀏覽器,就會基于XMLHttpRequests實作axios,如果是node.js環境,就會基于node內置核心模塊http實作axios
簡單來說,axios的基本原理就是
- axios還是屬于 XMLHttpRequest, 因此需要實作一個ajax,或者基于http ,
- 還需要一個promise物件來對結果進行處理,
有什么不理解的或者是建議歡迎評論提出.專案已經放到 github:github.com/Sunny-lucki…
二、基本使用方式
axios基本使用方式主要有
- axios(config)
- axios.method(url, data , config)
// index.html檔案
<html>
<script type="text/javascript" src="https://www.cnblogs.com/smileZAZ/archive/2022/09/19/axios"></script>
<body>
<button >點我發送請求</button>
<script>
document.querySelector('.btn').onclick = function() {
// 分別使用以下方法呼叫,查看myaxios的效果
axios.post('/postAxios', {
name: '小美post'
}).then(res => {
console.log('postAxios 成功回應', res);
})
axios({
method: 'post',
url: '/getAxios'
}).then(res => {
console.log('getAxios 成功回應', res);
})
}
</script>
</body>
</html>
</html>
三、實作axios和axios.method
從axios(config)的使用上可以看出匯出的axios是一個方法,從axios.method(url, data , config)的使用可以看出匯出的axios上或者原型上掛有get,post等方法
實際上匯出的axios就是一個Axios類中的一個方法,
如代碼所以,核心代碼是request,我們把request匯出,就可以使用axios(config)這種形式來呼叫axios了,
class Axios {
constructor() {
}
request(config) {
return new Promise(resolve => {
const {url = '', method = 'get', data = https://www.cnblogs.com/smileZAZ/archive/2022/09/19/{}} = config;
// 發送ajax請求
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.onload = function() {
console.log(xhr.responseText)
resolve(xhr.responseText);
}
xhr.send(data);
})
}
}
怎么匯出呢?十分簡單,new Axios,獲得axios實體,再獲得實體上的request方法就好了,
// 最侄訓出axios的方法,即實體的request方法
function CreateAxiosFn() {
let axios = new Axios();
let req = axios.request.bind(axios);
return req;
}
// 得到最后的全域變數axios
let axios = CreateAxiosFn();
點擊查看此時的myAxios.js
現在axios實際上就是request方法,
你可能會很疑惑,因為我當初看原始碼的時候也很疑惑:干嘛不直接寫個request方法,然后匯出呢?非得這樣繞這么大的彎子,別急,后面慢慢就會講到,
現在一個簡單的axios就完成了,我們來引入myAxios.js檔案并測驗一下可以使用不?
簡單的搭建服務器:
//server.js
var express = require('express');
var app = express();
//設定允許跨域訪問該服務.
app.all('*', function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type');
res.header('Access-Control-Allow-Methods', '*');
res.header('Content-Type', 'application/json;charset=utf-8');
next();
});
app.get('/getTest', function(request, response){
data = https://www.cnblogs.com/smileZAZ/archive/2022/09/19/{'FrontEnd':'前端',
'Sunny':'陽光'
};
response.json(data);
});
var server = app.listen(5000, function(){
console.log("服務器啟動");
});
index.html
//index.html
<script type="text/javascript" src="https://www.cnblogs.com/smileZAZ/archive/2022/09/19/myAxios.js"></script>
<body>
<button >點我發送請求</button>
<script>
document.querySelector('.btn').onclick = function() {
// 分別使用以下方法呼叫,查看myaxios的效果
axios({
method: 'get',
url: 'http://localhost:5000/getTest'
}).then(res => {
console.log('getAxios 成功回應', res);
})
}
</script>
</body>
點擊按鈕,看看是否能成功獲得資料,
可喜可賀,成功,
現在我們來實作下axios.method()的形式,
思路,我們可以再Axios.prototype添加這些方法,而這些方法內部呼叫request方法即可,如代碼所示:
// 定義get,post...方法,掛在到Axios原型上
const methodsArr = ['get', 'delete', 'head', 'options', 'put', 'patch', 'post'];
methodsArr.forEach(met => {
Axios.prototype[met] = function() {
console.log('執行'+met+'方法');
// 處理單個方法
if (['get', 'delete', 'head', 'options'].includes(met)) { // 2個引數(url[, config])
return this.request({
method: met,
url: arguments[0],
...arguments[1] || {}
})
} else { // 3個引數(url[,data[,config]])
return this.request({
method: met,
url: arguments[0],
data: arguments[1] || {},
...arguments[2] || {}
})
}
}
})
我們通過遍歷methodsArr陣列,依次在Axios.prototype添加對應的方法,注意的是'get', 'delete', 'head', 'options'這些方法只接受兩個引數,而其他的可接受三個引數,想一下也知道,get不把引數放body的,
但是,你有沒有發現,我們只是在Axios的prototype上添加對應的方法,我們匯出去的可是request方法啊,那怎么辦? 簡單,把Axios.prototype上的方法搬運到request上即可,
我們先來實作一個工具方法,實作將b的方法混入a;
const utils = {
extend(a,b, context) {
for(let key in b) {
if (b.hasOwnProperty(key)) {
if (typeof b[key] === 'function') {
a[key] = b[key].bind(context);
} else {
a[key] = b[key]
}
}
}
}
}
然后我們就可以利用這個方法將Axios.prototype上的方法搬運到request上啦,
我們修改一下之前的CreateAxiosFn方法即可
function CreateAxiosFn() {
let axios = new Axios();
let req = axios.request.bind(axios);
增加代碼
utils.extend(req, Axios.prototype, axios)
return req;
}
點擊查看此時的myAxios.js
現在來測驗一下能不能使用axios.get()這種形式呼叫axios,
<body>
<button >點我發送請求</button>
<script>
document.querySelector('.btn').onclick = function() {
axios.get('http://localhost:5000/getTest')
.then(res => {
console.log('getAxios 成功回應', res);
})
}
</script>
</body>
害,又是意料之中成功,
再完成下一個功能之前,先給上目前myAxios.js的完整代碼
class Axios {
constructor() {
}
request(config) {
return new Promise(resolve => {
const {url = '', method = 'get', data = https://www.cnblogs.com/smileZAZ/archive/2022/09/19/{}} = config;
// 發送ajax請求
console.log(config);
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.onload = function() {
console.log(xhr.responseText)
resolve(xhr.responseText);
}
xhr.send(data);
})
}
}
// 定義get,post...方法,掛在到Axios原型上
const methodsArr = ['get', 'delete', 'head', 'options', 'put', 'patch', 'post'];
methodsArr.forEach(met => {
Axios.prototype[met] = function() {
console.log('執行'+met+'方法');
// 處理單個方法
if (['get', 'delete', 'head', 'options'].includes(met)) { // 2個引數(url[, config])
return this.request({
method: met,
url: arguments[0],
...arguments[1] || {}
})
} else { // 3個引數(url[,data[,config]])
return this.request({
method: met,
url: arguments[0],
data: arguments[1] || {},
...arguments[2] || {}
})
}
}
})
// 工具方法,實作b的方法或屬性混入a;
// 方法也要混入進去
const utils = {
extend(a,b, context) {
for(let key in b) {
if (b.hasOwnProperty(key)) {
if (typeof b[key] === 'function') {
a[key] = b[key].bind(context);
} else {
a[key] = b[key]
}
}
}
}
}
// 最侄訓出axios的方法-》即實體的request方法
function CreateAxiosFn() {
let axios = new Axios();
let req = axios.request.bind(axios);
// 混入方法, 處理axios的request方法,使之擁有get,post...方法
utils.extend(req, Axios.prototype, axios)
return req;
}
// 得到最后的全域變數axios
let axios = CreateAxiosFn();
四、請求和回應攔截器
我們先看下攔截器的使用
// 添加請求攔截器
axios.interceptors.request.use(function (config) {
// 在發送請求之前做些什么
return config;
}, function (error) {
// 對請求錯誤做些什么
return Promise.reject(error);
});
// 添加回應攔截器
axios.interceptors.response.use(function (response) {
// 對回應資料做點什么
return response;
}, function (error) {
// 對回應錯誤做點什么
return Promise.reject(error);
});
攔截器是什么意思呢?其實就是在我們發送一個請求的時候會先執行請求攔截器的代碼,然后再真正地執行我們發送的請求,這個程序會對config,也就是我們發送請求時傳送的引數進行一些操作,
而當接收回應的時候,會先執行回應攔截器的代碼,然后再把回應的資料回傳來,這個程序會對response,也就是回應的資料進行一系列操作,
怎么實作呢?需要明確的是攔截器也是一個類,管理回應和請求,因此我們先實作攔截器
class InterceptorsManage {
constructor() {
this.handlers = [];
}
use(fullfield, rejected) {
this.handlers.push({
fullfield,
rejected
})
}
}
我們是用這個陳述句axios.interceptors.response.use和axios.interceptors.request.use,來觸發攔截器執行use方法的,
說明axios上有一個回應攔截器和一個請求攔截器,那怎么實作Axios呢?看代碼
class Axios {
constructor() {
新增代碼
this.interceptors = {
request: new InterceptorsManage,
response: new InterceptorsManage
}
}
request(config) {
return new Promise(resolve => {
const {url = '', method = 'get', data = https://www.cnblogs.com/smileZAZ/archive/2022/09/19/{}} = config;
// 發送ajax請求
console.log(config);
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.onload = function() {
console.log(xhr.responseText)
resolve(xhr.responseText);
};
xhr.send(data);
})
}
}
可見,axios實體上有一個物件interceptors,這個物件有兩個攔截器,一個用來處理請求,一個用來處理回應,
所以,我們執行陳述句axios.interceptors.response.use和axios.interceptors.request.use的時候,實作獲取axios實體上的interceptors物件,然后再獲取response或request攔截器,再執行對應的攔截器的use方法,
而執行use方法,會把我們傳入的回呼函式push到攔截器的handlers陣列里,
到這里你有沒有發現一個問題,這個interceptors物件是Axios上的啊,我們匯出的是request方法啊(欸?好熟悉的問題,上面提到過哈哈哈~~~額),處理方法跟上面處理的方式一樣,都是把Axios上的方法和屬性搬到request過去,也就是遍歷Axios實體上的方法,得以將interceptors物件掛載到request上,
所以只要更改下CreateAxiosFn方法即可,
function CreateAxiosFn() {
let axios = new Axios();
let req = axios.request.bind(axios);
// 混入方法, 處理axios的request方法,使之擁有get,post...方法
utils.extend(req, Axios.prototype, axios)
新增代碼
utils.extend(req, axios)
return req;
}
好了,現在request也有了interceptors物件,那么什么時候拿interceptors物件中的handler之前保存的回呼函式出來執行,
沒錯,就是我們發送請求的時候,會先獲取request攔截器的handlers的方法來執行,再執行我們發送的請求,然后獲取response攔截器的handlers的方法來執行,
因此,我們要修改之前所寫的request方法 之前是這樣的,
request(config) {
return new Promise(resolve => {
const {url = '', method = 'get', data = https://www.cnblogs.com/smileZAZ/archive/2022/09/19/{}} = config;
// 發送ajax請求
console.log(config);
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.onload = function() {
console.log(xhr.responseText)
resolve(xhr.responseText);
};
xhr.send(data);
})
}
但是現在request里不僅要執行發送ajax請求,還要執行攔截器handlers中的回呼函式,所以,最好下就是將執行ajax的請求封裝成一個方法
request(config) {
this.sendAjax(config)
}
sendAjax(config){
return new Promise(resolve => {
const {url = '', method = 'get', data = https://www.cnblogs.com/smileZAZ/archive/2022/09/19/{}} = config;
// 發送ajax請求
console.log(config);
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.onload = function() {
console.log(xhr.responseText)
resolve(xhr.responseText);
};
xhr.send(data);
})
}
好了,現在我們要獲得handlers中的回呼
request(config) {
// 攔截器和請求組裝佇列
let chain = [this.sendAjax.bind(this), undefined] // 成對出現的,失敗回呼暫時不處理
// 請求攔截
this.interceptors.request.handlers.forEach(interceptor => {
chain.unshift(interceptor.fullfield, interceptor.rejected)
})
// 回應攔截
this.interceptors.response.handlers.forEach(interceptor => {
chain.push(interceptor.fullfield, interceptor.rejected)
})
// 執行佇列,每次執行一對,并給promise賦最新的值
let promise = Promise.resolve(config);
while(chain.length > 0) {
promise = promise.then(chain.shift(), chain.shift())
}
return promise;
}
我們先把sendAjax請求和undefined放進了chain陣列里,再把請求攔截器的handlers的成對回呼放到chain陣列頭部,再把回應攔截器的handlers的承兌回呼反倒chain陣列的尾部,
然后再 逐漸取數 chain陣列的成對回呼執行,
promise = promise.then(chain.shift(), chain.shift())
這一句,實際上就是不斷將config從上一個promise傳遞到下一個promise,期間可能回呼config做出一些修改,什么意思?我們結合一個例子來講解一下
首先攔截器是這樣使用的
// 添加請求攔截器
axios.interceptors.request.use(function (config) {
// 在發送請求之前做些什么
return config;
}, function (error) {
// 對請求錯誤做些什么
return Promise.reject(error);
});
// 添加回應攔截器
axios.interceptors.response.use(function (response) {
// 對回應資料做點什么
return response;
}, function (error) {
// 對回應錯誤做點什么
return Promise.reject(error);
});
然后執行request的時候,chain陣列的資料是這樣的
chain = [
function (config) {
// 在發送請求之前做些什么
return config;
},
function (error) {
// 對請求錯誤做些什么
return Promise.reject(error);
}
this.sendAjax.bind(this),
undefined,
function (response) {
// 對回應資料做點什么
return response;
},
function (error) {
// 對回應錯誤做點什么
return Promise.reject(error);
}
]
首先
執行第一次promise.then(chain.shift(), chain.shift()),即
promise.then(
function (config) {
// 在發送請求之前做些什么
return config;
},
function (error) {
// 對請求錯誤做些什么
return Promise.reject(error);
}
)
一般情況,promise是resolved狀態,是執行成功回呼的,也就是執行
function (config) {
// 在發送請求之前做些什么
return config;
},
而promise.then是要回傳一個新的promise物件的,
為了區分,在這里,我會把這個新的promise物件叫做第一個新的promise物件 這個第一個新的promise物件會把
function (config) {
// 在發送請求之前做些什么
return config;
},
的執行結果傳入resolve函式中
resolve(config)
使得這個回傳的第一個新的promise物件的狀態為resovled,而且第一個新的promise物件的data為config,
這里需要對Promise的原理足夠理解,所以我前一篇文章寫的是手寫Promise核心原理,再也不怕面試官問我Promise原理,你可以去看看 接下來,再執行
promise.then( sendAjax(config) , undefined )
注意:這里的promise是 上面提到的第一個新的promise物件,
而promise.then這個的執行又會回傳第二個新的promise物件,
因為這里promise.then中的promise也就是第一個新的promise物件的狀態是resolved的,所以會執行sendAjax(),而且會取出第一個新的promise物件的data 作為config轉入sendAjax(),
當sendAjax執行完,就會回傳一個response,這個response就會保存在第二個新的promise物件的data中,
接下來,再執行
promise.then(
function (response) {
// 對回應資料做點什么
return response;
},
function (error) {
// 對回應錯誤做點什么
return Promise.reject(error);
}
)
同樣,會把第二個新的promise物件的data取出來作為response引數傳入
function (response) {
// 對回應資料做點什么
return response;
},
飯后回傳一個promise物件,這個promise物件的data保存了這個函式的執行結果,也就是回傳值response,
然后通過return promise;
把這個promise回傳了,咦?是怎么取出promise的data的,我們看看我們平常事怎么獲得回應資料的
axios.get('http://localhost:5000/getTest')
.then(res => {
console.log('getAxios 成功回應', res);
})
在then里接收回應資料,所以原理跟上面一樣,將回傳的promise的data作為res引數了,
現在看看我們的myAxios完整代碼吧,好有個全面的了解
class InterceptorsManage {
constructor() {
this.handlers = [];
}
use(fullfield, rejected) {
this.handlers.push({
fullfield,
rejected
})
}
}
class Axios {
constructor() {
this.interceptors = {
request: new InterceptorsManage,
response: new InterceptorsManage
}
}
request(config) {
// 攔截器和請求組裝佇列
let chain = [this.sendAjax.bind(this), undefined] // 成對出現的,失敗回呼暫時不處理
// 請求攔截
this.interceptors.request.handlers.forEach(interceptor => {
chain.unshift(interceptor.fullfield, interceptor.rejected)
})
// 回應攔截
this.interceptors.response.handlers.forEach(interceptor => {
chain.push(interceptor.fullfield, interceptor.rejected)
})
// 執行佇列,每次執行一對,并給promise賦最新的值
let promise = Promise.resolve(config);
while(chain.length > 0) {
promise = promise.then(chain.shift(), chain.shift())
}
return promise;
}
sendAjax(){
return new Promise(resolve => {
const {url = '', method = 'get', data = https://www.cnblogs.com/smileZAZ/archive/2022/09/19/{}} = config;
// 發送ajax請求
console.log(config);
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.onload = function() {
console.log(xhr.responseText)
resolve(xhr.responseText);
};
xhr.send(data);
})
}
}
// 定義get,post...方法,掛在到Axios原型上
const methodsArr = ['get', 'delete', 'head', 'options', 'put', 'patch', 'post'];
methodsArr.forEach(met => {
Axios.prototype[met] = function() {
console.log('執行'+met+'方法');
// 處理單個方法
if (['get', 'delete', 'head', 'options'].includes(met)) { // 2個引數(url[, config])
return this.request({
method: met,
url: arguments[0],
...arguments[1] || {}
})
} else { // 3個引數(url[,data[,config]])
return this.request({
method: met,
url: arguments[0],
data: arguments[1] || {},
...arguments[2] || {}
})
}
}
})
// 工具方法,實作b的方法混入a;
// 方法也要混入進去
const utils = {
extend(a,b, context) {
for(let key in b) {
if (b.hasOwnProperty(key)) {
if (typeof b[key] === 'function') {
a[key] = b[key].bind(context);
} else {
a[key] = b[key]
}
}
}
}
}
// 最侄訓出axios的方法-》即實體的request方法
function CreateAxiosFn() {
let axios = new Axios();
let req = axios.request.bind(axios);
// 混入方法, 處理axios的request方法,使之擁有get,post...方法
utils.extend(req, Axios.prototype, axios)
return req;
}
// 得到最后的全域變數axios
let axios = CreateAxiosFn();
來測驗下攔截器功能是否正常
<script type="text/javascript" src="https://www.cnblogs.com/smileZAZ/archive/2022/09/19/myAxios.js"></script>
<body>
<button >點我發送請求</button>
<script>
// 添加請求攔截器
axios.interceptors.request.use(function (config) {
// 在發送請求之前做些什么
config.method = "get";
console.log("被我請求攔截器攔截了,哈哈:",config);
return config;
}, function (error) {
// 對請求錯誤做些什么
return Promise.reject(error);
});
// 添加回應攔截器
axios.interceptors.response.use(function (response) {
// 對回應資料做點什么
console.log("被我回應攔截攔截了,哈哈 ");
response = {message:"回應資料被我替換了,啊哈哈哈"}
return response;
}, function (error) {
// 對回應錯誤做點什么
console.log("錯了嗎");
return Promise.reject(error);
});
document.querySelector('.btn').onclick = function() {
// 分別使用以下方法呼叫,查看myaxios的效果
axios({
url: 'http://localhost:5000/getTest'
}).then(res => {
console.log('response', res);
})
}
</script>
</body>
攔截成功!!!!!
本文轉載于:
https://juejin.cn/post/6856706569263677447
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/509145.html
標籤:其他
