前言
Axios是一個(gè)當(dāng)前使用較為廣泛师坎,封裝的功能較為完備的一個(gè)HTTP庫(kù)蟀悦,作為目前熱門的網(wǎng)絡(luò)請(qǐng)求庫(kù),學(xué)習(xí)和使用axios中的特性和功能也是前端開(kāi)發(fā)需要具備的能力。本篇文章將對(duì)axios的使用和二次封裝進(jìn)行講解啊掏,希望對(duì)各位有所幫助蠢络。
一衰猛、為什么要使用Axios
要想了解為什么要使用axios,我們不妨先說(shuō)說(shuō)發(fā)送異步請(qǐng)求都有什么方式
(1)Ajax
Ajax的核心是JavaScript對(duì)象XmlHttpRequest刹孔。該對(duì)象在Internet Explorer 5中首次引入啡省,它是一種支持異步請(qǐng)求的技術(shù).簡(jiǎn)而言之,XmlHttpRequest使您可以使用JavaScript向服務(wù)器提出請(qǐng)求并處理響應(yīng)髓霞,而不阻塞用戶卦睹。但Ajax的缺點(diǎn)也很明顯,使用起來(lái)復(fù)雜方库,很多東西都需要我們自己做二次封裝结序,所以隨著其他二次封裝的Http庫(kù)出現(xiàn)后,我們現(xiàn)在基本都不直接使用Ajax來(lái)做網(wǎng)絡(luò)請(qǐng)求了纵潦。
(2)JQuery
JQurey庫(kù)在網(wǎng)絡(luò)請(qǐng)求方面對(duì)Ajax進(jìn)行了二次封裝徐鹤,由于JQuery簡(jiǎn)單易用,功能齊全邀层,降低了我們進(jìn)行網(wǎng)絡(luò)請(qǐng)求的的復(fù)雜度返敬,在以前很長(zhǎng)一段時(shí)間中也是很多項(xiàng)目Http庫(kù)的選擇。但隨著技術(shù)的發(fā)展寥院,jQuery 也逐步走向一個(gè)衰弱的過(guò)程劲赠。越來(lái)越多的前端開(kāi)發(fā)者開(kāi)始使用諸如 Angular、React 和 Vue 這樣的新型框架秸谢。在一個(gè)基本用不到 jQuery 的技術(shù)中進(jìn)行前端開(kāi)發(fā)凛澎,為了要使用 jQuery 的 Ajax 相關(guān)方法而強(qiáng)行引入整個(gè) jQuery,這就有些得不償失估蹄,所以人們尋求體積更小塑煎、更加輕便的類庫(kù)。
(3)fetch
fetch也是當(dāng)前使用較多的網(wǎng)絡(luò)請(qǐng)求方式之一元媚,需要注意的是fetch并不是對(duì)Ajax的二次封裝轧叽,而是使用原生的JS進(jìn)行開(kāi)發(fā)的。fetch是基于 promise 進(jìn)行設(shè)計(jì)的刊棕,寫法上也更加的方便和簡(jiǎn)單炭晒,更為符合關(guān)注點(diǎn)分離的原則。但fetch的API 偏底層甥角,需要封裝;默認(rèn)不帶Cookie网严,需要手動(dòng)添加; 瀏覽器支持情況不是很友好。
(4)axios
axios在瀏覽器端實(shí)際上也是基于 XMLHttpRequest 來(lái)實(shí)現(xiàn)的嗤无,并且基于 promise 提供了一套 promise 風(fēng)格的鏈?zhǔn)秸{(diào)用 API震束,具備著支持promise API怜庸、支持請(qǐng)求和響應(yīng)攔截、提供并發(fā)請(qǐng)求接口功能垢村、輕量高效割疾、簡(jiǎn)單易用、客戶端支持防止CSRF等優(yōu)點(diǎn)嘉栓。也是當(dāng)前使用最為廣泛的類庫(kù)之一宏榕。
綜上,我們可以看見(jiàn)對(duì)于應(yīng)用Vue侵佃、React等框架的項(xiàng)目來(lái)說(shuō)麻昼,使用fetch和axios都是較為合理的選擇,但axios的兼容性和功能性暫時(shí)還是要略勝一籌馋辈,這也是我們?yōu)槭裁匆陧?xiàng)目中使用axios的原因抚芦。
要想在項(xiàng)目中使用axios,我們需要先在項(xiàng)目中安裝axios類庫(kù)
npm install axios
二迈螟、axios的使用
(一)axios的請(qǐng)求方式
axios提供了下面2種方式來(lái)讓我們創(chuàng)建對(duì)應(yīng)的請(qǐng)求
axios(config)
axios(url[, config])
我們可以看一下下面這個(gè)演示用例:
config中配置的參數(shù)是有關(guān)本次請(qǐng)求的詳細(xì)屬性叉抡,常見(jiàn)的需要配置的是url,method井联,以及params(get請(qǐng)求)或者data(post請(qǐng)求)卜壕。需要注意的是,axios默認(rèn)的請(qǐng)求方式是get烙常,所以如果是get請(qǐng)求轴捎,method可以省略。
axios({
url: 'http://httpbin.org/get',
method: 'get',
params: {
name: '小明',
age: 14
}
}).then(res => {
console.log(res)
}).catch(err => console.error)
等價(jià)于下面這種寫法:
axios('http://httpbin.org/get',{
params: {
name: '小明',
age: 14
}
}).then(res => {
console.log(res)
}).catch(err => console.error)
我們?cè)跒g覽器的控制臺(tái)查看axios返回的response結(jié)果蚕脏,可以看到axios幫助我們把請(qǐng)求和響應(yīng)結(jié)果都做了一個(gè)封裝侦副,實(shí)際上除了data
屬性外,其他的屬性都是axios為了讓我們更好地分析本次網(wǎng)絡(luò)請(qǐng)求的參數(shù)而做的封裝驼鞭,一般來(lái)說(shuō)我們更多是直接使用data
中的數(shù)據(jù)即可秦驯。
除了上面兩種封裝方式,我們還可以用axios提供的簡(jiǎn)寫方式來(lái)快速創(chuàng)建請(qǐng)求:
##### axios.request(config)
##### axios.get(url[, config])
##### axios.delete(url[, config])
##### axios.head(url[, config])
##### axios.post(url[, data[, config]])
##### axios.put(url[, data[, config]])
##### axios.patch(url[, data[, config]])
其實(shí)挣棕,上述的簡(jiǎn)寫方式以及前面提到的axios(config)
這種創(chuàng)建請(qǐng)求的方式译隘,本質(zhì)上調(diào)用的都是axios.request(config)
這個(gè)函數(shù),有興趣的同學(xué)可以自行看一下axios的源碼洛心。
然后下面我們就來(lái)使用簡(jiǎn)寫方式做一個(gè)小案例吧:
使用簡(jiǎn)寫方式發(fā)送一個(gè)post
請(qǐng)求固耘,post
請(qǐng)求要求我們提供3個(gè)參數(shù),分別是url词身、data和config厅目,如果不需要額外配置config則可以不寫,使用默認(rèn)值:
axios.post('http://httpbin.org/post',{name:'小明',age:14}).then(res=>{
console.log(res)
}).catch(err => console.error(err));
我們可以看一下簡(jiǎn)寫方式響應(yīng)的結(jié)果,其實(shí)和之前基本上是差不多的损敷,只是說(shuō)簡(jiǎn)寫方式讓我們用起來(lái)更加方便而已葫笼。
(二)axios結(jié)果的處理
axios執(zhí)行完網(wǎng)絡(luò)請(qǐng)求后,返回的結(jié)果是一個(gè)Promise
對(duì)象拗馒,常見(jiàn)的我們可以使用then
路星、catch
來(lái)分別處理請(qǐng)求成功和失敗的結(jié)果。
axios.get('http://httpbin.org/get',{
params:{
name:'小明',
age:14
}
}).then(res=>{
console.log(res);
}).catch(err=>{
console.log(err);
})
我們還可以使用ES7規(guī)范提出的await
瘟忱、async
關(guān)鍵字來(lái)解決異步的問(wèn)題:
需要注意的是奥额,使用await關(guān)鍵字的話,方法所在的函數(shù)需要加上async
async function xxx(){
try{
const result = await axios.get('http://httpbin.org/get', {
params: {
name: '小明',
age: 14
}
})
console.log(result);
}catch (err) {
console.log(err);
}
}
(三)axios.all()
的使用
axios支持我們使用axios.all()
來(lái)同時(shí)處理多個(gè)異步請(qǐng)求的場(chǎng)景:
比如下面我們想要在request1和request2都請(qǐng)求完之后访诱,對(duì)結(jié)果進(jìn)行輸出:
const request1 = axios.get('http://httpbin.org',{
params:{
name: '小明',
age: 14
}
});
const request2 = axios.get('http://httpbin.org',{
params:{
name: '小藍(lán)',
age: 15
}
});
axios.all([request1,request2]).then(res=>{
console.log(res);
}).catch(err=>{
console.log(err);
})
如果覺(jué)得返回對(duì)象是一個(gè)數(shù)組不好處理,我們可以通過(guò)數(shù)組的解構(gòu)賦值來(lái)解決這個(gè)問(wèn)題:
axios.all([request1,request2]).then(([res1,res2])=>{
console.log(res1);
console.log(res2);
}).catch(err=>{
console.log(err);
})
三韩肝、axios的配置
(一)請(qǐng)求配置選項(xiàng)
我們?cè)谝婚_(kāi)始講axios的API時(shí)触菜,就提到了axios(config)
這種創(chuàng)建方式,那么請(qǐng)求具體可以傳入哪些配置呢哀峻?
其實(shí)axios官方對(duì)這一塊已經(jīng)講得挺多的了涡相,這里的話我就摘抄部分重要的出來(lái)吧:
{
// `url` 是用于請(qǐng)求的服務(wù)器 URL
url: '/user',
// `method` 是創(chuàng)建請(qǐng)求時(shí)使用的方法
method: 'get', // default
// `baseURL` 將自動(dòng)加在 `url` 前面,除非 `url` 是一個(gè)絕對(duì) URL剩蟀。
// 它可以通過(guò)設(shè)置一個(gè) `baseURL` 便于為 axios 實(shí)例的方法傳遞相對(duì) URL
baseURL: 'https://some-domain.com/api/',
// `headers` 是即將被發(fā)送的自定義請(qǐng)求頭
headers: {'X-Requested-With': 'XMLHttpRequest'},
// `params` 是即將與請(qǐng)求一起發(fā)送的 URL 參數(shù)
// 必須是一個(gè)無(wú)格式對(duì)象(plain object)或 URLSearchParams 對(duì)象
params: {
ID: 12345
},
// `paramsSerializer` 是一個(gè)負(fù)責(zé) `params` 序列化的函數(shù)
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
paramsSerializer: function(params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},
// `data` 是作為請(qǐng)求主體被發(fā)送的數(shù)據(jù)
// 只適用于這些請(qǐng)求方法 'PUT', 'POST', 和 'PATCH'
// 在沒(méi)有設(shè)置 `transformRequest` 時(shí)催蝗,必須是以下類型之一:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 瀏覽器專屬:FormData, File, Blob
// - Node 專屬: Stream
data: {
firstName: 'Fred'
},
// `timeout` 指定請(qǐng)求超時(shí)的毫秒數(shù)(0 表示無(wú)超時(shí)時(shí)間)
// 如果請(qǐng)求話費(fèi)了超過(guò) `timeout` 的時(shí)間,請(qǐng)求將被中斷
timeout: 1000,
// `withCredentials` 表示跨域請(qǐng)求時(shí)是否需要使用憑證
withCredentials: false, // default
proxy: {
host: '127.0.0.1',
port: 9000,
auth: {
username: 'mikeymike',
password: 'rapunz3l'
}
}
}
(二)響應(yīng)結(jié)構(gòu)信息
響應(yīng)結(jié)構(gòu)指的是axios完成網(wǎng)絡(luò)請(qǐng)求后育特,返回的數(shù)據(jù)格式丙号,我們?cè)谥暗闹v解中就有大概講過(guò)。這里的話我們可以看一下摘抄自官方的各個(gè)數(shù)據(jù)的描述缰冤。
{
// `data` 由服務(wù)器提供的響應(yīng)
data: {},
// `status` 來(lái)自服務(wù)器響應(yīng)的 HTTP 狀態(tài)碼
status: 200,
// `statusText` 來(lái)自服務(wù)器響應(yīng)的 HTTP 狀態(tài)信息
statusText: 'OK',
// `headers` 服務(wù)器響應(yīng)的頭
headers: {},
// `config` 是為請(qǐng)求提供的配置信息
config: {},
// 'request'
// `request` is the request that generated this response
// It is the last ClientRequest instance in node.js (in redirects)
// and an XMLHttpRequest instance the browser
request: {}
}
(三)全局默認(rèn)配置
全局默認(rèn)配置是十分重要的知識(shí)點(diǎn)犬缨,其實(shí)很多時(shí)候項(xiàng)目中的請(qǐng)求基本上大部分配置都是一樣的,比如baseURL
棉浸、timeout
和content-type
等怀薛,如果我們每次發(fā)請(qǐng)求的時(shí)候都要手動(dòng)寫,那就很麻煩了迷郑。所以axios為我們提供了一種全局的配置枝恋,我們可以對(duì)axios進(jìn)行一些默認(rèn)請(qǐng)求的配置,這樣后續(xù)再請(qǐng)求的時(shí)候嗡害,我們就不需要再手寫baseURL等參數(shù)了焚碌。
如果不知道可以配置哪些默認(rèn)配置,可以看一下上面的請(qǐng)求配置或者官方文檔就漾。
axios.defaults.baseURL = 'http://httpbin.org';
axios.defaults.timeout = 5000;
axios.defaults.headers.common['Authorization'] = 'AUTH_TOKEN';
我們?cè)谏厦媾渲昧巳齻€(gè)axios的全局默認(rèn)配置呐能,現(xiàn)在我們發(fā)送一個(gè)請(qǐng)求后來(lái)觀察一下控制臺(tái)的輸出:
我們可以看到,使用了全局默認(rèn)配置之后,我們?cè)侔l(fā)送請(qǐng)求時(shí)摆出,就不需要額外寫一些重復(fù)性的請(qǐng)求配置了朗徊。
(四)自定義實(shí)例默認(rèn)配置
我們前端項(xiàng)目開(kāi)發(fā)中,可能會(huì)遇到有多個(gè)后端服務(wù)器的情況偎漫,針對(duì)這種情況我們單純地使用全局默認(rèn)配置可能就有點(diǎn)不方便了爷恳,比如有多個(gè)后端服務(wù)器所以baseURL就不能只設(shè)成一個(gè)。這種情況下象踊,axios還支持我們自定義多個(gè)實(shí)例來(lái)滿足我們的需要温亲。
自定義實(shí)例后對(duì)于實(shí)例的默認(rèn)配置有下面2種方式,一般來(lái)說(shuō)杯矩,我們用的第一種比較多
// Set config defaults when creating the instance
const instance = axios.create({
baseURL: 'https://api.example.com'
});
// Alter defaults after instance has been created
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
(五)配置的優(yōu)先順序
有些讀者可能會(huì)有疑問(wèn)栈虚,對(duì)于timeout
配置如果在多個(gè)地方進(jìn)行了不同的配置,那么會(huì)以哪個(gè)為準(zhǔn)史隆。
實(shí)際上配置會(huì)以一個(gè)優(yōu)先順序進(jìn)行合并魂务。這個(gè)順序是:在 lib/defaults.js 找到的庫(kù)的默認(rèn)值,然后是實(shí)例的 defaults 屬性泌射,最后是請(qǐng)求的 config 參數(shù)粘姜。后者將優(yōu)先于前者。這里是一個(gè)例子:
// 使用由庫(kù)提供的配置的默認(rèn)值來(lái)創(chuàng)建實(shí)例
// 此時(shí)超時(shí)配置的默認(rèn)值是 `0`
var instance = axios.create();
// 覆寫庫(kù)的超時(shí)默認(rèn)值
// 現(xiàn)在熔酷,在超時(shí)前孤紧,所有請(qǐng)求都會(huì)等待 2.5 秒
instance.defaults.timeout = 2500;
// 為已知需要花費(fèi)很長(zhǎng)時(shí)間的請(qǐng)求覆寫超時(shí)設(shè)置
instance.get('/longRequest', {
timeout: 5000
});
四、 axios攔截器
axios為我們提供了請(qǐng)求和響應(yīng)的攔截器拒秘,方便我們?cè)谡?qǐng)求或響應(yīng)被 then 或 catch 處理前做一些特殊的處理号显。實(shí)際上,我們?cè)陧?xiàng)目中也經(jīng)常會(huì)使用到axios攔截器來(lái)做一些譬如請(qǐng)求結(jié)果的二次封裝等操作翼抠。
(一)請(qǐng)求攔截器
我們可能會(huì)如下場(chǎng)景中在請(qǐng)求攔截器中做一些操作:
1. 發(fā)送網(wǎng)絡(luò)請(qǐng)求時(shí)咙轩,在界面的中間位置顯示Loading的組件
2. 某一些請(qǐng)求要求用戶必須攜帶token,如果沒(méi)有攜帶阴颖,那么直接跳轉(zhuǎn)到登錄頁(yè)面
3. 對(duì)params/data序列化的操作
// 添加請(qǐng)求攔截器
axios.interceptors.request.use(function (config) {
// 在發(fā)送請(qǐng)求之前做些什么
return config;
}, function (error) {
// 對(duì)請(qǐng)求錯(cuò)誤做些什么
return Promise.reject(error);
});
(二)響應(yīng)攔截器
對(duì)于響應(yīng)攔截器活喊,我們做的操作一般是根據(jù)返回的狀態(tài)碼來(lái)做一下不同的處理以及對(duì)請(qǐng)求的結(jié)果做一下二次的封裝。
// 添加響應(yīng)攔截器
axios.interceptors.response.use(function (response) {
// 對(duì)響應(yīng)數(shù)據(jù)做點(diǎn)什么
return response;
}, function (error) {
// 對(duì)響應(yīng)錯(cuò)誤做點(diǎn)什么
return Promise.reject(error);
});
五量愧、axios的二次封裝
為什么要對(duì)axios做二次封裝
在上面的講解中钾菊,相信大家已經(jīng)知道了axios的使用,但實(shí)際開(kāi)發(fā)中偎肃,我們并不會(huì)直接使用axios來(lái)創(chuàng)建網(wǎng)絡(luò)請(qǐng)求煞烫,而是會(huì)先對(duì)axios做一下二次封裝,原因很簡(jiǎn)單累颂,如果沒(méi)有對(duì)axios做二次封裝的話滞详,一旦后面項(xiàng)目需要更改網(wǎng)絡(luò)請(qǐng)求的類庫(kù)那么改造的難度就相當(dāng)大了凛俱,我們需要在項(xiàng)目中一點(diǎn)點(diǎn)地把使用到axios的api修改掉,而如果我們一開(kāi)始就對(duì)axios做了二次封裝的話料饥,那么其他文件就只需要用我們二次封裝的api來(lái)操作就行蒲犬,即使后面項(xiàng)目更改了網(wǎng)絡(luò)請(qǐng)求的類庫(kù),我們也只需要修改二次封裝的那個(gè)文件就行岸啡。
由于不同的公司的二次封裝風(fēng)格不同原叮,這里的話我只簡(jiǎn)單以個(gè)人為例,做些常見(jiàn)配置:
在src目錄下創(chuàng)建api
目錄巡蘸,專門用于存放網(wǎng)絡(luò)請(qǐng)求的api:
其中奋隶,config.js
用于存放對(duì)axios默認(rèn)配置的常量,這樣方便我們后續(xù)查看和修改:
const devBaseURL = 'http://localhost:3000';
const proBaseURL = 'http://xxxx.xxxx.xxxx.xxxx:3000'
export const BASE_URL = process.env.NODE_ENV === 'development'? devBaseURL : proBaseURL;
export const TIMEOUT = 5000;
export const CONTENT_TYPE = 'application/x-www-form-urlencoded';
http.js
用于存放我們對(duì)axios的默認(rèn)配置
/**
* 這個(gè)文件用于封裝axios
*/
import axios from 'axios';
import {BASE_URL,TIMEOUT,CONTENT_TYPE} from './config';
axios.defaults.baseURL = BASE_URL;
axios.defaults.timeout = TIMEOUT;
axios.defaults.headers.common['Content-Type'] = CONTENT_TYPE;
//響應(yīng)攔截器
axios.interceptors.response.use(
response => {
//如果reponse里面的status是200悦荒,說(shuō)明訪問(wèn)到接口了唯欣,否則錯(cuò)誤
if(response.status == 200){
return Promise.resolve(response);
}else{
return Promise.reject(response);
}
},
error => {
if(error.response.status){
return Promise.reject(error.response);
}
}
);
/**
* 封裝get方法
*/
export function get(url,params={}){
return new Promise((resolve,reject) => {
axios.get(url,{params:params})
.then(response =>{
resolve(response.data);
})
.catch(err =>{
reject(err);
})
});
}
/**
* 封裝post方法
*/
export function post(url,data={},config){
return new Promise((resolve,reject) => {
axios.post(url,data,config)
.then(response =>{
resolve(response.data);
})
.catch(err =>{
reject(err);
})
});
}
index.js
文件用于存放具體的接口
import {get,post} from './http';
// 獲取token
export const getTransferToken = (data) => post('/xxx',data) ;
// 獲取
export const getAudio = (data) => post('/xxx',data,{responseType: 'blob'}) ;
至此,對(duì)于axios的使用我們就介紹到這里了逾冬,對(duì)于更多細(xì)節(jié)的學(xué)習(xí)黍聂,大家可以自行參考axios官方:
官網(wǎng)鏈接:http://www.axios-js.com/zh-cn/docs/