在前端日常开发中除了纯静态展示页面,必不可少的就是做一些接口请求,从XMLHttpRequest
,到jQuery
的ajax
,再到后来的Fetch
和Axios
。
为什么选择Axios
- 从浏览器中创建 XMLHttpRequests
- 从 node.js 创建 http 请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换 JSON 数据
- 客户端支持防御 XSRF
为什么不用Fetch
- fetchtch只对网络请求报错,对400,500都当做成功的请求,需要封装去处理
- fetch默认不会带cookie,需要添加配置项(Axios也需要)
- fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行
- fetch没有办法原生监测请求的进度
基本配置
安装
# npm
$ npm install axios
# yarn
$ yarn add axios
# cdn
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
复制代码
直接使用
1、使用.
执行某个请求方法,[method name]
包含了request
、head
、get
、put
、delete
、post
、patch
。
axios[method name](url[, data[, config]])
复制代码
2、使用传入config
对象的方式调用
axios([config])
.then(response => response)
.catch(error => error)
// config 可选内容的配置选项。只有 url 是必需的。如果没有指定 method,请求将默认使用 get 方法。
{
// `url` 是用于请求的服务器 URL
url: ‘/user‘,
// `method` 是创建请求时使用的方法
method: ‘get‘, // 默认是 get
// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
baseURL: ‘https://some-domain.com/api/‘,
// `headers` 是即将被发送的自定义请求头
headers: {‘X-Requested-With‘: ‘XMLHttpRequest‘},
// `params` 是即将与请求一起发送的 URL 参数
// 必须是一个无格式对象(plain object)或 URLSearchParams 对象
params: {
ID: 12345
},
...,
// `cancelToken` 指定用于取消请求的 cancel token
// (查看后面的 Cancellation 这节了解更多)
cancelToken: new CancelToken(function (cancel) {
})
复制代码
- 更多配置项请移步
Axios
官网,官网配置
3、并发处理
axios.all(iterable)
axios.spread(callback)
function getUserAccount() {
return axios.get(‘/user/12345‘);
}
function getUserPermissions() {
return axios.get(‘/user/12345/permissions‘);
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// 两个请求都执行完成
}));
复制代码
创建实例使用
- 通过
axios.create
创建请求实例,可接收一个config
对象;
import axios from axios;
var instance = axios.create([config]);
复制代码
默认config
配置
1、配置请求超时时间
# 使用defaults
axios.defaults.timeout = 30000 // 时间以毫秒为单位
# 使用实例传入config
var instance = axios.create({
timeout: 30000
});
复制代码
2、配置请求携带cookie
# 使用defaults
axios.defaults.withCredentials = true // true为自动携带
# 使用实例传入config
var instance = axios.create({
withCredentials: true
});
复制代码
3、配置baseURL
baseURL
将自动加在url
前面,除非url
是一个绝对 URL。
# 使用defaults
axios.defaults.baseURL = ‘https://github.com/detanx‘
# 使用实例传入config
var instance = axios.create({
baseURL: ‘https://github.com/detanx‘
});
复制代码
4、配置headers
- 例如设置
token
# 使用defaults
axios.defaults.headers.common[‘Authorization‘] = AUTH_TOKEN;
axios.defaults.headers.post[‘Content-Type‘] = ‘application/x-www-form-urlencoded‘;
# 使用实例传入config
var instance = axios.create({
headers: {
‘Content-Type‘: ‘application/x-www-form-urlencoded‘,
‘Authorization‘: AUTH_TOKEN
});
复制代码
5、取消请求cancelToken
- (1)使用
CancelToken.source
工厂方法创建cancelToken
。
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get(url, {
cancelToken: source.token
}).catch(thrown => thrown);
// 取消请求(message 参数是可选的)
source.cancel(‘cancel.‘);
复制代码
- (2)通过传递一个
executor
函数到CancelToken
的构造函数来创建cancelToken
。
const CancelToken = axios.CancelToken;
let cancel;
axios.get(url, {
cancelToken: new CancelToken(function executor(c) {
// executor 函数接收一个 cancel 函数作为参数
cancel = c;
})
})
// 取消请求(message 参数是可选的)
cancel(‘cancel.‘);
复制代码
6、更多
拦截器配置及错误处理
- 在请求或响应被
then
或catch
处理前拦截它们。
请求拦截
- 你可以直接添加到
import
进来的axios
上面,一般建议新建Axios
实例,将默认值及拦截器挂载到实例上。
const NotCancleUrl = [‘/stockserver/user/login-user‘]; // 不取消重复请求的url列表
const pendingRequestArray = []; // 声明一个数组用于存储每个请求的取消函数和请求标识
const { CancelToken } = axios;
const removePending = config => {
for (const index in pendingRequestArray) {
const key = `${config.url}&${config.method}`;
if (pendingRequestArray[index].key === key && !NotCancleUrl.includes(config.url)) {
// 当当前请求在数组中存在时执行函数体
pendingRequestArray[index].cancel(); // 执行取消操作
pendingRequestArray.splice(index, 1); // 把这条记录从数组中移除
}
}
};
// 添加请求拦截器
axios.interceptors.request.use( config => {
// 例如添加取消重复请求
removePending(config); // 在一个发送请求前执行一下取消操作
config.cancelToken = new CancelToken(c => {
// 这里的请求标识是用请求地址&请求方式拼接的字符串,你可以选择其他的一些方式
pendingRequestArray.push({ key: `${config.url}&${config.method}`, cancel: c });
});
return config;
}, function (error) {
// 处理请求错误
return Promise.reject(error);
});
复制代码
响应拦截
1、 上面在请求拦截中做了重复请求的拦截,如果请求成功,我们应该在请求成功后,将相应的请求从pendingRequestArray
缓存请求的数组中移除。
// 添加响应拦截器
axios.interceptors.response.use(
// 处理响应数据
response => {
removePending(response.config); // 请求成功后,从pendingRequestArray中移除
return response;
},
// 处理响应错误
error => {
return Promise.reject(error);
}
);
复制代码
2、对相应的请求超时、401
、403
状态做同一处理(当然你也可以根据你项目中实际的应用做一些其他的处理)。
axios.interceptors.response.use(
response => response,
error => {
// 服务请求超时
if (error === undefined || error.code === ‘ECONNABORTED‘) {
message.warning(‘服务请求超时‘);
return Promise.reject(error);
}
const {
response: { status }
} = error;
const { response } = error;
const info = response.data;
// 401未登录
if (status === 401) {
// 未登录去重定向登录页面
...
}
// 403访问被拒
if (status === 403) {
...
}
return Promise.reject(error);
}
)
复制代码
错误处理
1、在请求的catch
方法中处理
axios.get(url)
.catch(function (error) {
if (error.response) {
const { data, status, headers } = error.response;
// 请求已发出,但服务器响应的状态码不在 2xx 范围内
console.log(data, status, headers);
} else {
// Something happened in setting up the request that triggered an Error
console.log(‘Error‘, error.message);
}
console.log(error.config);
});
复制代码
2、使用validateStatus
配置选项定义一个自定义HTTP
状态码的错误范围。
axios.get(url), {
validateStatus: function (status) {
return status < 500; // 状态码在大于或等于500时才会 reject
}
})
复制代码
项目中应用
- 默认设置及拦截器在之前已讲过,下面就只说请求方法实例封装。
通用使用封装
// 请求方法
export const AXIOS_METHOD_TYPET = {
GET: ‘GET‘,
POST: ‘POST‘,
PUT: ‘PUT‘,
DELETE: ‘DELETE‘,
PATCH: ‘PATCH‘
};
/** 方法说明
* @method request
* @param api 请求相对地址
* @param method 请求方法,默认get
* @param params 请求参数 默认为空
* @param config 请求配置 默认为空
* @return function 返回axios实例
*/
const request = (api, method = AXIOS_METHOD_TYPET.GET, params = {}, config = {}) => {
method = method.toLocaleUpperCase();
// get请求放在params中,其他请求放在body
const data = method === ‘GET‘ ? ‘params‘ : ‘data‘;
// 这部分也可以放到defaults中去设置
let headers = {
‘X-Requested-With‘: ‘XMLHttpRequest‘,
‘Content-Type‘: ‘application/json‘
};
if (config.headers) {
headers = {
...headers,
...config.headers
};
}
return axios({
url: api,
method,
[data]: params,
headers
});
};
export default request;
复制代码
使用方式
// index.js
import request from ‘request‘;
request(‘/user/login‘, post, {
username: ‘detanx‘,
password: ‘123456‘
}).then(response => console.log(response))
.catch(error => console.log(error))
复制代码
hooks
调用封装
// useRequest.js
import { useState, useEffect } from ‘react‘;
// instance 为myAxios中暴露的添加了默认方法及拦截器的axios实例
import { instance, AXIOS_METHOD_TYPET } from ‘./myAxios‘;
/** 方法说明
* @method useRequest
* @return [
responseData, // 请求成功的返回
errorData, // 请求失败返回
loading, // 请求是否完成
setRequestParams // 参数改变重新请求
]
*/
const useRequest = (url, method = AXIOS_METHOD_TYPET.GET, params = {}, config = {}) => {
method = method.toLocaleUpperCase();
const data = method === ‘GET‘ ? ‘params‘ : ‘data‘;
const [responseData, setResponseData] = useState({});
const [errorData, setErrorData] = useState({});
const [loading, setLoading] = useState(true);
const [requestParams, setRequestParams] = useState(params);
let headers = {
‘X-Requested-With‘: ‘XMLHttpRequest‘,
‘Content-Type‘: ‘application/json‘
};
if (config.headers) {
headers = {
...headers,
...config.headers
};
}
useEffect(() => {
instance({
url,
method,
[data]: requestParams,
headers
})
.then(({ data }) => {
setLoading(false);
setResponseData(data);
})
.catch(err => {
setLoading(false);
setErrorData(err);
});
}, [requestParams]);
return [
responseData,
errorData,
loading,
setRequestParams
];
};
export default useRequest;
复制代码
使用方式
// index.js
import useRequest from ‘useRequest‘;
expoert default () => {
const [
responseData,
errorData,
loading,
setRequestParams
] = useRequest(‘/api/user‘, ‘get‘)
const clickHandle = () => {
setRequestParams({name: ‘detanx‘})
}
return (<>
<button onClick={clickHandle}>新参数请求</button>
</>)
}
hooks
封装完善
- 上面的封装会存在一个问题,就是每次使用
useRequest
都会直接发起一个请求,有些页面进来其实不需要请求,例如纯登录页面,只需要在提交登录信息时才需要发起请求,所以对上面的封装进行优化。约定params
为false
不发起请求。
// 修改params的默认值为false及默认不发送请求
const useRequest = (url, method = AXIOS_METHOD_TYPET.GET, params = false, config = {}) => {
...
useEffect(() => {
// 约定`params`为`false`不发起请求
+ if (requestParams === false) return;
instance()
}, [requestParams]);
...
};
https://weibo.com/ttarticle/p/show?id=2309404499443872301253