1、关键
axios有几种调用方式,
一种是 axios({}),以函数的方式,传入config就好;
一种是axios.get(), 不只是get,还有axios.request(), axios.post()等
即又有对象属性,又可以用函数方式调用,而 Axios是一个大类,所以可以用bind。
Axios new出来的实例是一个对象,
用bind去生成一个函数,并把Axios上的方法与属性拷贝到实例instance上,就可以用axios()或者
axios.get()去调用
2、使用:
// 方式 1 axios(config)
axios({
method: 'post',
url: '/user/123',
data: {
firstName: 'Fred',
lastName: 'feld'
}
})
// 方式 2 axios(url[, config]),默认 get 请求
axios('http://xxx');
// 方式 3 使用别名进行请求
axios.request(config)
// axios.get(url[, config])
// axios.post(url[, data[, config]])
// axios.put(url[, data[, config]])
// ...
// 方式 4 创建 axios 实例,自定义配置
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
// axios#request(config)
// axios#get(url[, config])
// axios#post(url[, data[, config]])
// axios#put(url[, data[, config]])
// ...
可见,当我们调用axios()时,实际上是执行了createInstance返回的一个指向Axios.prototype.request的函数;通过添加create方法支持用户自定义配置创建,并且最终也是执行了Axios.prototype.request方法。
3、源码实现
lib/axios.js
let defaults = require('./defaults')
let utils = require('./utils') // 上一篇有公共方法
function createInstance(defaultConfig) {
// 创建 axios 实例
var context = new Axios(defaultConfig);
// 把 instance 指向 Axios.prototype.request 方法
// bind返回一个新的函数,内部去执行request
var instance = bind(Axios.prototype.request, context);
// extend 将第二个参数对象的方法拷贝到第一个对象上去
// 把 Axios.prototype(原型对象) 上的方法扩展到 instance 上,指定上下文是 context
// 方法有:request()/get()/post()/put()/delete()
utils.extend(instance, Axios.prototype, context);
// 把 context 上的方法扩展到 instance 上
// 把 Axios实例对象 上的方法扩展到 instance 上,有defaults 和 interceptors属性
utils.extend(instance, context);
// 导出 instance 对象
return instance;
}
实例一个 axios,并添加create方法
var axios = createInstance(defaults);
// 添加 create 方法,返回 createInstance 函数,参数为自定义配置 + 默认配置
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
// ...此处有关拦截的
// 导出实例 axios
module.exports = axios;
// Allow use of default import syntax in TypeScript
module.exports.default = axios;
可见,当我们调用axios()时,实际上是执行了createInstance返回的一个指向Axios.prototype.request的函数;通过添加create方法支持用户自定义配置创建,并且最终也是执行了Axios.prototype.request方法。
也就是说 axios.js 中导出的 axios 对象并不是 new Axios() 方法返回的对象 context,
而是 Axios.prototype.request.bind(context) 执行返回的 instance,
通过遍历 Axios.prototype 并改变其 this 指向到 context;
遍历 context 对象让 instance 对象具有 context 的所有属性。这样 instance 对象就无敌了,😎 既拥有了 Axios.prototype 上的所有方法,又具有了 context 的所有属性。
为什么不能直接用实例对象给我们调用呢?
Axios.js
// 1、配置:外部传入,可覆盖内部默认配置
function Axios(instanceConfig) {
// 配置
this.defaults = instanceConfig;
// ...拦截器实例
}
在看看原型方法 request 做了什么
1、支持多类型传参
2、配置优先级定义
3、通过 promise
链式调用,依次顺序执行
Axios.prototype.request = function request(config) {
// 为了支持 request(url, {...}), request({url, ...})
// 方式二:axios('https://xxxx') ,判断参数字符串,则赋值给 config.url
if (typeof config === 'string') {
config = arguments[1] || {};
config.url = arguments[0];
} else {
// 方式一:axios({}) ,参数为对象,则直接赋值给 config
config = config || {};
}
// 配置优先级: 调用方法的配置 > 实例化axios的配置 > 默认配置
// 举个例子,类似:axios.get(url, {}) > axios.create(url, {}) > 内部默认设置
config = mergeConfig(this.defaults, config);
// ...此处关于拦截器链的,具体看下篇
var promise;
// 传入配置
promise = Promise.resolve(config);
return promise;
};
对于delete’, ‘get’, ‘head’, 'options,不需要一个个Axios.prototype.delete…去定义,他们的不同除了方法,还有传参,所以只需分成两类,以 Axios.prototype[method] 方式去定义
// Provide aliases for supported request methods utils.forEach(数组, 函数)-> 循环数组并执行函数
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
}));
};
});
// 与平时用的forEach的区别:对象也可以传,并遍历出对象的键值
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
}));
};
});