???個人主頁:前端青山
??系列專欄:Vue篇
??人終將被年少不可得之物困其一生
依舊青山,本期給大家?guī)韛ue篇專欄內(nèi)容:vue-axios
目錄
一、axios是什么
二、實現(xiàn)一個簡易版axios
三、源碼分析
小結(jié)
四、axios的使用
特性
基本使用
五、為什么要封裝
六、如何封裝
設置接口請求前綴
設置請求頭與超時時間
封裝請求方法
請求攔截器
響應攔截器
一、axios是什么
關于axios
的基本使用,上篇文章已經(jīng)有所涉及,這里再稍微回顧下:
發(fā)送請求
import axios from 'axios';
?
axios(config) // 直接傳入配置
axios(url[, config]) // 傳入url和配置
axios[method](url[, option]) // 直接調(diào)用請求方式方法,傳入url和配置
axios[method](url[, data[, option]]) // 直接調(diào)用請求方式方法,傳入data、url和配置
axios.request(option) // 調(diào)用 request 方法
?
const axiosInstance = axios.create(config)
// axiosInstance 也具有以上 axios 的能力
?
axios.all([axiosInstance1, axiosInstance2]).then(axios.spread(response1, response2))
// 調(diào)用 all 和傳入 spread 回調(diào)
請求攔截器
axios.interceptors.request.use(function (config) {
? ?// 這里寫發(fā)送請求前處理的代碼
? ?return config;
}, function (error) {
? ?// 這里寫發(fā)送請求錯誤相關的代碼
? ?return Promise.reject(error);
});
響應攔截器
axios.interceptors.response.use(function (response) {
? ?// 這里寫得到響應數(shù)據(jù)后處理的代碼
? ?return response;
}, function (error) {
? ?// 這里寫得到錯誤響應處理的代碼
? ?return Promise.reject(error);
});
取消請求
// 方式一
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
?
axios.get('xxxx', {
?cancelToken: source.token
})
// 取消請求 (請求原因是可選的)
source.cancel('主動取消請求');
?
// 方式二
const CancelToken = axios.CancelToken;
let cancel;
?
axios.get('xxxx', {
?cancelToken: new CancelToken(function executor(c) {
? ?cancel = c;
})
});
cancel('主動取消請求');
二、實現(xiàn)一個簡易版axios
構建一個Axios
構造函數(shù),核心代碼為request
class Axios {
? ?constructor() {
?
? }
?
? ?request(config) {
? ? ? ?return new Promise(resolve => {
? ? ? ? ? ?const {url = '', method = 'get', data = {}} = config;
? ? ? ? ? ?// 發(fā)送ajax請求
? ? ? ? ? ?const xhr = new XMLHttpRequest();
? ? ? ? ? ?xhr.open(method, url, true);
? ? ? ? ? ?xhr.onload = function() {
? ? ? ? ? ? ? ?console.log(xhr.responseText)
? ? ? ? ? ? ? ?resolve(xhr.responseText);
? ? ? ? ? }
? ? ? ? ? ?xhr.send(data);
? ? ? })
? }
}
導出axios
實例
// 定義get,post...方法,掛在到Axios原型上
const methodsArr = ['get', 'delete', 'head', 'options', 'put', 'patch', 'post'];
methodsArr.forEach(met => {
? ?Axios.prototype[met] = function() {
? ? ? ?console.log('執(zhí)行'+met+'方法');
? ? ? ?// 處理單個方法
? ? ? ?if (['get', 'delete', 'head', 'options'].includes(met)) { // 2個參數(shù)(url[, config])
? ? ? ? ? ?return this.request({
? ? ? ? ? ? ? ?method: met,
? ? ? ? ? ? ? ?url: arguments[0],
? ? ? ? ? ? ? ?...arguments[1] || {}
? ? ? ? ? })
? ? ? } else { // 3個參數(shù)(url[,data[,config]])
? ? ? ? ? ?return this.request({
? ? ? ? ? ? ? ?method: met,
? ? ? ? ? ? ? ?url: arguments[0],
? ? ? ? ? ? ? ?data: arguments[1] || {},
? ? ? ? ? ? ? ?...arguments[2] || {}
? ? ? ? ? })
? ? ? }
?
? }
})
上述就已經(jīng)能夠?qū)崿F(xiàn)axios({ })
這種方式的請求
下面是來實現(xiàn)下axios.method()
這種形式的請求
// 定義get,post...方法,掛在到Axios原型上
const methodsArr = ['get', 'delete', 'head', 'options', 'put', 'patch', 'post'];
methodsArr.forEach(met => {
? ?Axios.prototype[met] = function() {
? ? ? ?console.log('執(zhí)行'+met+'方法');
? ? ? ?// 處理單個方法
? ? ? ?if (['get', 'delete', 'head', 'options'].includes(met)) { // 2個參數(shù)(url[, config])
? ? ? ? ? ?return this.request({
? ? ? ? ? ? ? ?method: met,
? ? ? ? ? ? ? ?url: arguments[0],
? ? ? ? ? ? ? ?...arguments[1] || {}
? ? ? ? ? })
? ? ? } else { // 3個參數(shù)(url[,data[,config]])
? ? ? ? ? ?return this.request({
? ? ? ? ? ? ? ?method: met,
? ? ? ? ? ? ? ?url: arguments[0],
? ? ? ? ? ? ? ?data: arguments[1] || {},
? ? ? ? ? ? ? ?...arguments[2] || {}
? ? ? ? ? })
? ? ? }
?
? }
})
將Axios.prototype
上的方法搬運到request
上
首先實現(xiàn)個工具類,實現(xiàn)將b
方法混入到a
,并且修改this
指向
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]
? ? ? }
? ? }
? ? ?
? }
}
}
修改導出的方法
function CreateAxiosFn() {
let axios = new Axios();
let req = axios.request.bind(axios);
// 增加代碼
utils.extend(req, Axios.prototype, axios)
return req;
}
構建攔截器的構造函數(shù)
class InterceptorsManage {
constructor() {
this.handlers = [];
}
use(fullfield, rejected) {
this.handlers.push({
fullfield,
rejected
})
}
}
實現(xiàn)axios.interceptors.response.use
和axios.interceptors.request.use
class Axios {
constructor() {
// 新增代碼
this.interceptors = {
request: new InterceptorsManage,
response: new InterceptorsManage
}
}
request(config) {
...
}
}
執(zhí)行語句axios.interceptors.response.use
和axios.interceptors.request.use
的時候,實現(xiàn)獲取axios
實例上的interceptors
對象,然后再獲取response
或request
攔截器,再執(zhí)行對應的攔截器的use
方法
把Axios
上的方法和屬性搬到request
過去
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;
}
現(xiàn)在request
也有了interceptors
對象,在發(fā)送請求的時候,會先獲取request
攔截器的handlers
的方法來執(zhí)行
首先將執(zhí)行ajax
的請求封裝成一個方法
request(config) {
this.sendAjax(config)
}
sendAjax(config){
return new Promise(resolve => {
const {url = '', method = 'get', data = {}} = config;
// 發(fā)送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
中的回調(diào)
request(config) {
// 攔截器和請求組裝隊列
let chain = [this.sendAjax.bind(this), undefined] // 成對出現(xiàn)的,失敗回調(diào)暫時不處理
// 請求攔截
this.interceptors.request.handlers.forEach(interceptor => {
chain.unshift(interceptor.fullfield, interceptor.rejected)
})
// 響應攔截
this.interceptors.response.handlers.forEach(interceptor => {
chain.push(interceptor.fullfield, interceptor.rejected)
})
// 執(zhí)行隊列,每次執(zhí)行一對,并給promise賦最新的值
let promise = Promise.resolve(config);
while(chain.length > 0) {
promise = promise.then(chain.shift(), chain.shift())
}
return promise;
}
chains
大概是['fulfilled1','reject1','fulfilled2','reject2','this.sendAjax','undefined','fulfilled2','reject2','fulfilled1','reject1']
這種形式
這樣就能夠成功實現(xiàn)一個簡易版axios
三、源碼分析
首先看看目錄結(jié)構
axios
發(fā)送請求有很多實現(xiàn)的方法,實現(xiàn)入口文件為axios.js
function createInstance(defaultConfig) {
var context = new Axios(defaultConfig);
// instance指向了request方法,且上下文指向context,所以可以直接以 instance(option) 方式調(diào)用
// Axios.prototype.request 內(nèi)對第一個參數(shù)的數(shù)據(jù)類型判斷,使我們能夠以 instance(url, option) 方式調(diào)用
var instance = bind(Axios.prototype.request, context);
// 把Axios.prototype上的方法擴展到instance對象上,
// 并指定上下文為context,這樣執(zhí)行Axios原型鏈上的方法時,this會指向context
utils.extend(instance, Axios.prototype, context);
// Copy context to instance
// 把context對象上的自身屬性和方法擴展到instance上
// 注:因為extend內(nèi)部使用的forEach方法對對象做for in 遍歷時,只遍歷對象本身的屬性,而不會遍歷原型鏈上的屬性
// 這樣,instance 就有了 defaults、interceptors 屬性。
utils.extend(instance, context);
return instance;
}
// Create the default instance to be exported 創(chuàng)建一個由默認配置生成的axios實例
var axios = createInstance(defaults);
// Factory for creating new instances 擴展axios.create工廠函數(shù),內(nèi)部也是 createInstance
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
// Expose all/spread
axios.all = function all(promises) {
return Promise.all(promises);
};
axios.spread = function spread(callback) {
return function wrap(arr) {
return callback.apply(null, arr);
};
};
module.exports = axios;
主要核心是 Axios.prototype.request
,各種請求方式的調(diào)用實現(xiàn)都是在 request
內(nèi)部實現(xiàn)的, 簡單看下 request
的邏輯
Axios.prototype.request = function request(config) {
// Allow for axios('example/url'[, config]) a la fetch API
// 判斷 config 參數(shù)是否是 字符串,如果是則認為第一個參數(shù)是 URL,第二個參數(shù)是真正的config
if (typeof config === 'string') {
config = arguments[1] || {};
// 把 url 放置到 config 對象中,便于之后的 mergeConfig
config.url = arguments[0];
} else {
// 如果 config 參數(shù)是否是 字符串,則整體都當做config
config = config || {};
}
// 合并默認配置和傳入的配置
config = mergeConfig(this.defaults, config);
// 設置請求方法
config.method = config.method ? config.method.toLowerCase() : 'get';
/*
something... 此部分會在后續(xù)攔截器單獨講述
*/
};
// 在 Axios 原型上掛載 'delete', 'get', 'head', 'options' 且不傳參的請求方法,實現(xiàn)內(nèi)部也是 request
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
Axios.prototype[method] = function(url, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url
}));
};
});
// 在 Axios 原型上掛載 'post', 'put', 'patch' 且傳參的請求方法,實現(xiàn)內(nèi)部同樣也是 request
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
Axios.prototype[method] = function(url, data, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url,
data: data
}));
};
});
request
入口參數(shù)為config
,可以說config
貫徹了axios
的一生
axios
中的 config
主要分布在這幾個地方:
-
默認配置
defaults.js
-
config.method
默認為get
-
調(diào)用
createInstance
方法創(chuàng)建axios
實例,傳入的config
-
直接或間接調(diào)用
request
方法,傳入的confi
?
// axios.js
// 創(chuàng)建一個由默認配置生成的axios實例
var axios = createInstance(defaults);
// 擴展axios.create工廠函數(shù),內(nèi)部也是 createInstance
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
// Axios.js
// 合并默認配置和傳入的配置
config = mergeConfig(this.defaults, config);
// 設置請求方法
config.method = config.method ? config.method.toLowerCase() : 'get';
?
從源碼中,可以看到優(yōu)先級:默認配置對象default
< method:get
< Axios
的實例屬性this.default
< request
參數(shù)
下面重點看看request
方法
Axios.prototype.request = function request(config) {
/*
先是 mergeConfig ... 等,不再闡述
*/
// Hook up interceptors middleware 創(chuàng)建攔截器鏈. dispatchRequest 是重中之重,后續(xù)重點
var chain = [dispatchRequest, undefined];
// push各個攔截器方法 注意:interceptor.fulfilled 或 interceptor.rejected 是可能為undefined
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
// 請求攔截器逆序 注意此處的 forEach 是自定義的攔截器的forEach方法
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
// 響應攔截器順序 注意此處的 forEach 是自定義的攔截器的forEach方法
chain.push(interceptor.fulfilled, interceptor.rejected);
});
// 初始化一個promise對象,狀態(tài)為resolved,接收到的參數(shù)為已經(jīng)處理合并過的config對象
var promise = Promise.resolve(config);
// 循環(huán)攔截器的鏈
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift()); // 每一次向外彈出攔截器
}
// 返回 promise
return promise;
};
攔截器interceptors
是在構建axios
實例化的屬性
function Axios(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(), // 請求攔截
response: new InterceptorManager() // 響應攔截
};
}
InterceptorManager構造函數(shù)
// 攔截器的初始化 其實就是一組鉤子函數(shù)
function InterceptorManager() {
this.handlers = [];
}
// 調(diào)用攔截器實例的use時就是往鉤子函數(shù)中push方法
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1;
};
// 攔截器是可以取消的,根據(jù)use的時候返回的ID,把某一個攔截器方法置為null
// 不能用 splice 或者 slice 的原因是 刪除之后 id 就會變化,導致之后的順序或者是操作不可控
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null;
}
};
// 這就是在 Axios的request方法中 中循環(huán)攔截器的方法 forEach 循環(huán)執(zhí)行鉤子函數(shù)
InterceptorManager.prototype.forEach = function forEach(fn) {
utils.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
}
請求攔截器方法是被 unshift
到攔截器中,響應攔截器是被push
到攔截器中的。最終它們會拼接上一個叫dispatchRequest
的方法被后續(xù)的 promise
順序執(zhí)行
var utils = require('./../utils');
var transformData = require('./transformData');
var isCancel = require('../cancel/isCancel');
var defaults = require('../defaults');
var isAbsoluteURL = require('./../helpers/isAbsoluteURL');
var combineURLs = require('./../helpers/combineURLs');
// 判斷請求是否已被取消,如果已經(jīng)被取消,拋出已取消
function throwIfCancellationRequested(config) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
}
module.exports = function dispatchRequest(config) {
throwIfCancellationRequested(config);
// 如果包含baseUrl, 并且不是config.url絕對路徑,組合baseUrl以及config.url
if (config.baseURL && !isAbsoluteURL(config.url)) {
// 組合baseURL與url形成完整的請求路徑
config.url = combineURLs(config.baseURL, config.url);
}
config.headers = config.headers || {};
// 使用/lib/defaults.js中的transformRequest方法,對config.headers和config.data進行格式化
// 比如將headers中的Accept,Content-Type統(tǒng)一處理成大寫
// 比如如果請求正文是一個Object會格式化為JSON字符串,并添加application/json;charset=utf-8的Content-Type
// 等一系列操作
config.data = transformData(
config.data,
config.headers,
config.transformRequest
);
// 合并不同配置的headers,config.headers的配置優(yōu)先級更高
config.headers = utils.merge(
config.headers.common || {},
config.headers[config.method] || {},
config.headers || {}
);
// 刪除headers中的method屬性
utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);
// 如果config配置了adapter,使用config中配置adapter的替代默認的請求方法
var adapter = config.adapter || defaults.adapter;
// 使用adapter方法發(fā)起請求(adapter根據(jù)瀏覽器環(huán)境或者Node環(huán)境會有不同)
return adapter(config).then(
// 請求正確返回的回調(diào)
function onAdapterResolution(response) {
// 判斷是否以及取消了請求,如果取消了請求拋出以取消
throwIfCancellationRequested(config);
// 使用/lib/defaults.js中的transformResponse方法,對服務器返回的數(shù)據(jù)進行格式化
// 例如,使用JSON.parse對響應正文進行解析
response.data = transformData(
response.data,
response.headers,
config.transformResponse
);
return response;
},
// 請求失敗的回調(diào)
function onAdapterRejection(reason) {
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
if (reason && reason.response) {
reason.response.data = transformData(
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(reason);
}
);
};
再來看看axios
是如何實現(xiàn)取消請求的,實現(xiàn)文件在CancelToken.js
function CancelToken(executor) {
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
// 在 CancelToken 上定義一個 pending 狀態(tài)的 promise ,將 resolve 回調(diào)賦值給外部變量 resolvePromise
var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
var token = this;
// 立即執(zhí)行 傳入的 executor函數(shù),將真實的 cancel 方法通過參數(shù)傳遞出去。
// 一旦調(diào)用就執(zhí)行 resolvePromise 即前面的 promise 的 resolve,就更改promise的狀態(tài)為 resolve。
// 那么xhr中定義的 CancelToken.promise.then方法就會執(zhí)行, 從而xhr內(nèi)部會取消請求
executor(function cancel(message) {
// 判斷請求是否已經(jīng)取消過,避免多次執(zhí)行
if (token.reason) {
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}
CancelToken.source = function source() {
// source 方法就是返回了一個 CancelToken 實例,與直接使用 new CancelToken 是一樣的操作
var cancel;
var token = new CancelToken(function executor(c) {
cancel = c;
});
// 返回創(chuàng)建的 CancelToken 實例以及取消方法
return {
token: token,
cancel: cancel
};
};
實際上取消請求的操作是在 xhr.js
中也有響應的配合的
if (config.cancelToken) {
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
// 取消請求
request.abort();
reject(cancel);
});
}
巧妙的地方在 CancelToken
中 executor
函數(shù),通過resolve
函數(shù)的傳遞與執(zhí)行,控制promise
的狀態(tài)
小結(jié)
四、axios的使用
axios
是一個輕量的 HTTP
客戶端
基于 XMLHttpRequest
服務來執(zhí)行 HTTP
請求,支持豐富的配置,支持 Promise
,支持瀏覽器端和 Node.js
端。自Vue
2.0起,尤大宣布取消對 vue-resource
的官方推薦,轉(zhuǎn)而推薦 axios
?,F(xiàn)在 axios
已經(jīng)成為大部分 Vue
開發(fā)者的首選
特性
-
從瀏覽器中創(chuàng)建
XMLHttpRequests
-
從
node.js
創(chuàng)建http
請求 -
支持
Promise
API -
攔截請求和響應
-
轉(zhuǎn)換請求數(shù)據(jù)和響應數(shù)據(jù)
-
取消請求
-
自動轉(zhuǎn)換
JSON
數(shù)據(jù) -
客戶端支持防御
XSRF
基本使用
安裝
// 項目中安裝
npm install axios --S
// cdn 引入
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
導入
import axios from 'axios'
發(fā)送請求
axios({ ? ? ? ?
?url:'xxx', ? ?// 設置請求的地址
?method:"GET", // 設置請求方法
?params:{ ? ? ?// get請求使用params進行參數(shù)憑借,如果是post請求用data
? ?type: '',
? ?page: 1
}
}).then(res => { ?
?// res為后端返回的數(shù)據(jù)
?console.log(res); ?
})
并發(fā)請求axios.all([])
function getUserAccount() {
? ?return axios.get('/user/12345');
}
?
function getUserPermissions() {
? ?return axios.get('/user/12345/permissions');
}
?
axios.all([getUserAccount(), getUserPermissions()])
? .then(axios.spread(function (res1, res2) {
? ?// res1第一個請求的返回的內(nèi)容,res2第二個請求返回的內(nèi)容
? ?// 兩個請求都執(zhí)行完成才會執(zhí)行
}));
五、為什么要封裝
axios
的 API 很友好,你完全可以很輕松地在項目中直接使用。
不過隨著項目規(guī)模增大,如果每發(fā)起一次HTTP
請求,就要把這些比如設置超時時間、設置請求頭、根據(jù)項目環(huán)境判斷使用哪個請求地址、錯誤處理等等操作,都需要寫一遍
這種重復勞動不僅浪費時間,而且讓代碼變得冗余不堪,難以維護。為了提高我們的代碼質(zhì)量,我們應該在項目中二次封裝一下 axios
再使用
舉個例子:
axios('http://localhost:3000/data', {
?// 配置代碼
?method: 'GET',
?timeout: 1000,
?withCredentials: true,
?headers: {
? ?'Content-Type': 'application/json',
? ?Authorization: 'xxx',
},
?transformRequest: [function (data, headers) {
? ?return data;
}],
?// 其他請求配置...
})
.then((data) => {
?// todo: 真正業(yè)務邏輯代碼
?console.log(data);
}, (err) => {
?// 錯誤處理代碼 ?
?if (err.response.status === 401) {
?// handle authorization error
}
?if (err.response.status === 403) {
?// handle server forbidden error
}
?// 其他錯誤處理.....
?console.log(err);
});
如果每個頁面都發(fā)送類似的請求,都要寫一堆的配置與錯誤處理,就顯得過于繁瑣了
這時候我們就需要對axios
進行二次封裝,讓使用更為便利
六、如何封裝
封裝的同時,你需要和 后端協(xié)商好一些約定,請求頭,狀態(tài)碼,請求超時時間.......
設置接口請求前綴:根據(jù)開發(fā)、測試、生產(chǎn)環(huán)境的不同,前綴需要加以區(qū)分
請求頭 : 來實現(xiàn)一些具體的業(yè)務,必須攜帶一些參數(shù)才可以請求(例如:會員業(yè)務)
狀態(tài)碼: 根據(jù)接口返回的不同status
, 來執(zhí)行不同的業(yè)務,這塊需要和后端約定好
請求方法:根據(jù)get
、post
等方法進行一個再次封裝,使用起來更為方便
請求攔截器: 根據(jù)請求的請求頭設定,來決定哪些請求可以訪問
響應攔截器: 這塊就是根據(jù) 后端`返回來的狀態(tài)碼判定執(zhí)行不同業(yè)務
設置接口請求前綴
利用node
環(huán)境變量來作判斷,用來區(qū)分開發(fā)、測試、生產(chǎn)環(huán)境
if (process.env.NODE_ENV === 'development') {
?axios.defaults.baseURL = 'http://dev.xxx.com'
} else if (process.env.NODE_ENV === 'production') {
?axios.defaults.baseURL = 'http://prod.xxx.com'
}
在本地調(diào)試的時候,還需要在vue.config.js
文件中配置devServer
實現(xiàn)代理轉(zhuǎn)發(fā),從而實現(xiàn)跨域
devServer: {
? ?proxy: {
? ? ?'/proxyApi': {
? ? ? ?target: 'http://dev.xxx.com',
? ? ? ?changeOrigin: true,
? ? ? ?pathRewrite: {
? ? ? ? ?'/proxyApi': ''
? ? ? }
? ? }
? }
}
設置請求頭與超時時間
大部分情況下,請求頭都是固定的,只有少部分情況下,會需要一些特殊的請求頭,這里將普適性的請求頭作為基礎配置。當需要特殊請求頭時,將特殊請求頭作為參數(shù)傳入,覆蓋基礎配置
const service = axios.create({
? ?...
? ?timeout: 30000, ?// 請求 30s 超時
?headers: {
? ? ? ?get: {
? ? ? ? ?'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
? ? ? ? ?// 在開發(fā)中,一般還需要單點登錄或者其他功能的通用請求頭,可以一并配置進來
? ? ? },
? ? ? ?post: {
? ? ? ? ?'Content-Type': 'application/json;charset=utf-8'
? ? ? ? ?// 在開發(fā)中,一般還需要單點登錄或者其他功能的通用請求頭,可以一并配置進來
? ? ? }
},
})
封裝請求方法
先引入封裝好的方法,在要調(diào)用的接口重新封裝成一個方法暴露出去
// get 請求
export function httpGet({
url,
params = {}
}) {
return new Promise((resolve, reject) => {
axios.get(url, {
params
}).then((res) => {
resolve(res.data)
}).catch(err => {
reject(err)
})
})
}
// post
// post請求
export function httpPost({
url,
data = {},
params = {}
}) {
return new Promise((resolve, reject) => {
axios({
url,
method: 'post',
transformRequest: [function (data) {
let ret = ''
for (let it in data) {
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
}
return ret
}],
// 發(fā)送的數(shù)據(jù)
data,
// url參數(shù)
params
}).then(res => {
resolve(res.data)
})
})
}
把封裝的方法放在一個api.js
文件中
import { httpGet, httpPost } from './http'
export const getorglist = (params = {}) => httpGet({ url: 'apps/api/org/list', params })
頁面中就能直接調(diào)用
// .vue
import { getorglist } from '@/assets/js/api'
getorglist({ id: 200 }).then(res => {
console.log(res)
})
這樣可以把api
統(tǒng)一管理起來,以后維護修改只需要在api.js
文件操作即可
請求攔截器
請求攔截器可以在每個請求里加上token,做了統(tǒng)一處理后維護起來也方便文章來源:http://www.zghlxwxcb.cn/news/detail-820132.html
// 請求攔截器
axios.interceptors.request.use(
config => {
// 每次發(fā)送請求之前判斷是否存在token
// 如果存在,則統(tǒng)一在http請求的header都加上token,這樣后臺根據(jù)token判斷你的登錄情況,此處token一般是用戶完成登錄后儲存到localstorage里的
token && (config.headers.Authorization = token)
return config
},
error => {
return Promise.error(error)
})
響應攔截器
響應攔截器可以在接收到響應后先做一層操作,如根據(jù)狀態(tài)碼判斷登錄狀態(tài)、授權文章來源地址http://www.zghlxwxcb.cn/news/detail-820132.html
// 響應攔截器
axios.interceptors.response.use(response => {
// 如果返回的狀態(tài)碼為200,說明接口請求成功,可以正常拿到數(shù)據(jù)
// 否則的話拋出錯誤
if (response.status === 200) {
if (response.data.code === 511) {
// 未授權調(diào)取授權接口
} else if (response.data.code === 510) {
// 未登錄跳轉(zhuǎn)登錄頁
} else {
return Promise.resolve(response)
}
} else {
return Promise.reject(response)
}
}, error => {
// 我們可以在這里對異常狀態(tài)作統(tǒng)一處理
if (error.response.status) {
// 處理請求失敗的情況
// 對不同返回碼對相應處理
return Promise.reject(error.response)
}
})
到了這里,關于vue中axios的介紹及封裝(看這一篇就夠了!)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!