axios 从何而来?
我们知道可以通过require('axios').default;的方式来加载 axios,该实例是通过createInstance函数所创建,代码如下:
function createInstance(defaultConfig) {
var context = new Axios(defaultConfig);
// 这里先省去其他函数对context的扩展
return context;
}
var axios = createInstance(defaults);
创建 Axios 类
通过上面代码,我们知道Axios是核心类,正是通过该类来构造axios 实例,然后我们就可以通过axios.get()等方式来进行 http 请求。
我们先创建一个 axios 文件夹,该文件夹作为项目根目录,然后在根目下创建 core 文件夹,在 core 文件夹里面创建 Axios.js 文件。
Axios类接收一个instanceConfig参数,该参数为axios实例的默认配置对象,该默认对象用来设置请求参数,比如请求超时时间等,我们之后会详细介绍这个对象。
我们在Axios的原型上添加一个request方法,axios中所有的http请求,比如Get、Post请求都是对该方法的封装调用,所有该方法非常重要。request方法接收一个自定义的配置对象,在该配置对象中可以定义请求的 url、请求 method 等内容;该方法也可以接收一个字符串,该字符串代表的是请求 url。
var dispatchRequest = require("./dispatchRequest");
/**
* axios实例构造函数
*
* @param {Object} instanceConfig 实例配置对象
*/
function Axios(instanceConfig) {
this.defaults = instanceConfig;
}
// 参数为url字符串: axios.request('/user/12345')
// 参数为对象:axios.request({ method: 'post', url: '/user/12345', data: { a: 'a'}})
Axios.prototype.request = function request(configOrUrl, config) {
// 这里是为了把config统一成一种数据格式
if (typeof configOrUrl === "string") {
// config是字符串则表明config参数是请求url
// 我们可通过axios('/url', config?)的方式来调用request方法
// 因此第二个参数可以是一个config对象,也可以不存在
config = config || {};
config.url = configOrUrl;
} else {
config = config || {};
}
// 设置http请求的类型, 优先使用config对象中的值,其次为默认对象的值,否则为get请求
if (config.method) {
config.method = config.method.toLowerCase();
} else if (this.defaults.method) {
config.method = this.defaults.method.toLowerCase();
} else {
config.method = "get";
}
var chain = [dispatchRequest, undefined];
var promise = Promise.resolve(config);
while (chain.length) {
// 相当于 promise = Promise.resolve(config).then(dispatchRequest, undefined)
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};
根据上面的代码,可知实际请求是在dispatchRequest方法中发出的,我们接着实现该函数
我们在 core 文件夹下创建 dispatchRequest.js 文件,该函数接收一个配置对象(即 Promise.resolve(config))。dispatchRequest 函数通过调用adapter方法发起 http 请求,该方法可以通过 config 对象进行自定义,如果没有自定义,则从默认配置文件中获取
dispatchRequest 函数返回值为 promise, 这样子我们就可以在请求外面异步处理响应数据
// 发出一个请求
const promise = axios.request("/url");
promise.then(
(res) => {
// res为onAdapterResolution回调的response返回值
// 处理请求成功
},
(err) => {
// err为onAdapterResolution回调的reason返回值
// 处理请求失败
}
);
上面的代码就是我们平时在工作中的实际用法,这就是通过 promise 来处理异步的强大之处,下面的代码为 dispatchRequest 函数的实现
var defaults = require("../defaults");
module.exports = function dispatchRequest(config) {
// 如果config中不包含adapter,那就用默认的adapter(适配器)
var adapter = config.adapter || defaults.adapter;
// 在adapter函数中发起请求
// onAdapterResolution回调接收请求成功后的数据
// onAdapterRejection回调接收请求失败后的数据
// adapter函数返回一个promise, 这些数据可以被request函数中的promise捕获
return adapter(config).then(
function onAdapterResolution(response) {
return response;
},
function onAdapterRejection(reason) {
return Promise.reject(reason);
}
);
};
我们在根目录下创建 defaults 目录,该目录下通过index.js导出一个默认配置对象,目前该对象只包含 adapter 方法
// 获取浏览器或node环境下发http请求的adapter
// 我们只讲浏览器部分
function getDefaultAdapter() {
var adapter;
if (typeof XMLHttpRequest !== "undefined") {
// 用于发起浏览器xhr请求
adapter = require("../adapters/xhr");
}
return adapter;
}
var defaults = {
adapter: getDefaultAdapter(),
};
module.exports = defaults;
我们在下一节实现adapter的内容