JS的http通信工具:axios
2020-07-29 本文已影响0人
乌云老思
基于Promise的HTTP库,用于发送Ajax,用简单的使用方法实现原生JS的xhr的功能,并且前后端皆可用
准备
安装
npm -i axios
导入
- 后端使用时:
const axios = require('axios')
- 前端使用时:
找到node_modules->axios->dist,将axios.min.js文件复制到static目录,然后在index.html引入
<script src='./axios.min.js'></script>
基本使用
函数方式
// get请求:
let data = {
name:"xiao"
}
let res = await axios({
url: '/getData', // 向url发送get请求
params: data, // 可携带参数,会自动转化为QueryString
})
console.log(res.data) // 后端传回的数据
// post请求:
let data = {
name:"xiao"
}
let res = await axios({
url: '/postData', // 向url发送请求
method: 'post', // 指定psot请求
data, // 携带数据
})
console.log(res.data) // 后端传回的数据
对象方式
// get请求:
let res = await axios.get('/getData')
console.log(res.data)
// post请求:
let data = {
name:"xiao"
}
let res = await axios.post('/postData',data)
console.log(res.data)
高级用法
拦截器
拦截器分为:
- 请求拦截器
- 响应拦截器
可以拦截所有的request或response,并对其内容进行统一修饰或执行其他操作,比如:
- 携带鉴权凭证
// 1.请求拦截,以设置token为例
axios.interceptors.request.use((config) => {
console.log(config)
const token = localStorage.getItem("token")
// 统一添加鉴权信息
if(token) config.headers.authorization = token
return config
})
// 2.响应拦截,以鉴权失败为例
axios.interceptors.response.use((res) => {
console.log(res)
return res
},(err) => {
console.log(err)
if(err.response.status==401)
// 当出现特定错误代码,弹窗提示
alert("please login")
return Promise.reject(err)
})
注意:多个请求拦截并列时逆序执行,多个响应拦截并列时顺序执行。
自定义环境切换和超时时间
自定义axios,通过axios.create()来实现:
- 开发环境和线上环境的切换
- 请求超时时间的自定义
const baseURLMap = {
DEV: 'http://localhost:8080',
PROD: 'http://localhost:80',
}
const currentEnv = 'DEV'
console.log(baseURLMap[currentEnv])
const myAxios = axios.create({
// 自定义开发环境与线上环境
baseURL: baseURLMap[currentEnv],
// 自定义超时时间,超过此时间服务器还不响应就报错超时
timeout: 2000,
})
源码分析
对象调用和函数调用的实现
axios使用同一个命名就可以实现对象调用和函数调用两种功能,我们来探究一下原理。
源码@lib/axios.js
var bind = require('./helpers/bind');
var Axios = require('./core/Axios');
var defaults = require('./defaults');
function createInstance(defaultConfig) {
// 将Axios实例化为context
var context = new Axios(defaultConfig);
// Axios.prototype.request是一个方法,将Axios.prototype.request和context绑定在instance中
var instance = bind(Axios.prototype.request, context);
// Copy axios.prototype to instance
utils.extend(instance, Axios.prototype, context);
// Copy context to instance
utils.extend(instance, context);
// 这样返回的instance就同时有方法和对象的功能
return instance;
}
// Create the default instance to be exported
var axios = createInstance(defaults);
// 最后导出axios
module.exports = axios;
对象和方法都来自Axios类,继续看一下这个类。
源码@lib/core/Axios.js
function Axios(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
// 这里是对象功能的实现,可以发现本质上还是调用的Axios.prototype.request
// Provide aliases for supported request methods
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url
}));
};
});
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function(url, data, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url,
data: data
}));
};
});
module.exports = Axios;
结论:axios的对象调用和函数调用,本质上都是调用Axios.prototype.request实现的。
拦截器的实现
多次添加请求拦截器会逆序执行,而多次添加响应拦截器则是顺序执行。源码中给出了原理。
源码@lib/core/Axios.js
Axios.prototype.request = function request(config) {
/*eslint no-param-reassign:0*/
// Allow for axios('example/url'[, config]) a la fetch API
if (typeof config === 'string') {
config = arguments[1] || {};
config.url = arguments[0];
} else {
config = config || {};
}
config = mergeConfig(this.defaults, config);
// Set config.method
if (config.method) {
config.method = config.method.toLowerCase();
} else if (this.defaults.method) {
config.method = this.defaults.method.toLowerCase();
} else {
config.method = 'get';
}
// Hook up interceptors middleware
var chain = [dispatchRequest, undefined];
var promise = Promise.resolve(config);
// 请求拦截从调用链前面添加函数,链式调用时,后添加的先执行
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
// 响应拦截从调用链后面添加函数,链式调用时,先添加的先执行
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
// 按调用链的顺序执行
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};
结论:请求拦截从调用链前面添加函数,链式调用时,后添加的先执行;响应拦截从调用链后面添加函数,链式调用时,先添加的先执行。
配置优先级
配置项通过一定的规则合并,request config
> instance.defaults
> 系统默认,优先级高的覆盖优先级低的。
// 第一种情况,创建一个实例,系统默认配置timeout为0,在此可以设置为自定义
var instance = axios.create({
timeout: 2000,
});
// 第二种情况,通过instance.defaults重新设置超时时间为2.5s,因为优先级比系统默认高
instance.defaults.timeout = 2500;
// 第三种情况,通过request config重新设置超时时间为5s,因为优先级比instance.defaults和系统默认都高
instance.get('/longRequest', {
timeout: 5000
});
根据源码可以了解到:
- 一般情况下使用系统默认配置
lib/defaults.js
作为参数创建实例 - 而在用
axios.create()
创建实例时,传入两个配置参数,前者还是默认配置,后者是用户输入的配置,这时使用lib/core/mergeConfig.js
合并两个配置,使第一种情况生效 - 通过传入配置参数创建的实例(包括系统默认配置),其配置信息储存在
lib/core/Axios.js
的defaults
属性中,所以可以通过直接修改instance.defaults
属性来修改其配置,使第二种情况生效 - 在
lib/core/Axios.js
的Axios.prototype.request()
中,又进行了一次自定义配置合并,可以合并临时传入的配置参数,使第三种情况生效
支持node和浏览器两种环境
源码中lib/defaults.js
文件里有getDefaultAdapter()
这个方法,用来判断环境。如果是浏览器就实例new XMLHttpRequest()
来发送请求响应服务,node环境就引用http和https库处理和响应http服务。
源码@lib/defaults.js
function getDefaultAdapter() {
var adapter;
if (typeof XMLHttpRequest !== 'undefined') {
// For browsers use XHR adapter
adapter = require('./adapters/xhr');
} else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
// For node use HTTP adapter
adapter = require('./adapters/http');
}
return adapter;
}