交接的项目刚好用到了axios,来看一下axios的源码,先贴一下核心文件的部分代码
function createInstance(defaultConfig) {
var context = new Axios(defaultConfig);
var instance = bind(Axios.prototype.request, context);
utils.extend(instance, Axios.prototype, context);
utils.extend(instance, context);
return instance;
}
var axios = createInstance(defaults);
axios.Axios = Axios;
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');
axios.all = function all(promises) {
return Promise.all(promises);
};
axios.spread = require('./helpers/spread');
我们用的axios就是createInstance的返回值,返回值是一个函数,传入的defauts是默认的config,包括header,contentType等。
Axios是一个class,他的prototype上的方法很重要,比如发起请求等。createInstance内通过utils.extends将Axios.prototype的方法拷贝到axios函数上。
axios.create就是createInstance调用,优势在于传入自定义的config增强配置
最后就是spread,all等方法,这两个方法实际上功能相同,实现略有区别。
概括一下axios的关系
createInstance
createInstance源码如下
function createInstance(defaultConfig) {
var context = new Axios(defaultConfig);
var instance = bind(Axios.prototype.request, context);
utils.extend(instance, Axios.prototype, context);
utils.extend(instance, context);
return instance;
}
大概过程就是创建一个contenxt,然后让request的this指向context,然后使用extends增强instance函数。
首先看一下defaultConfig
var defaults = {
adapter: getDefaultAdapter(),
transformRequest: [function transformRequest(data, headers) {
normalizeHeaderName(headers, 'Accept');
normalizeHeaderName(headers, 'Content-Type');
if () {return data;}
if () {}
if () {}
if () {}
return data;
}],
transformResponse: [function transformResponse(data) {
if (typeof data === 'string') {
try {
data = JSON.parse(data);
} catch (e) { /* Ignore */ }
}
return data;
}],
timeout: 0,
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
maxContentLength: -1,
validateStatus: function validateStatus(status) {
return status >= 200 && status < 300;
}
};
defaults.headers = {
common: {
'Accept': 'application/json, text/plain, */*'
}
};
utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {
defaults.headers[method] = {};
});
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
});
defaultConfig包含的是发起请求的基本配置,下面仔细看一下每一项
defaultConfig -> getDefaultAdapter
function getDefaultAdapter() {
var adapter;
if (typeof XMLHttpRequest !== 'undefined') {
adapter = require('./adapters/xhr');
} else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
adapter = require('./adapters/http');
}
return adapter;
}
getDefaultAdapter根据不同的环境(node or navigator)返回不同的adapter对象,adapter用于发起ajax请求
看一下adapter对象的结构
function xhrAdapter(config){
return new Promise(...)
}
xhrAdapter函数接收一个config对象,返回一下promise
if (utils.isFormData(requestData)) {
delete requestHeaders['Content-Type']; // Let the browser set it
}
这里判断一下data的类型,如果是formData就交给浏览器自己设置
if (config.auth) {
var username = config.auth.username || '';
var password = config.auth.password || '';
requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
}
验证一下config.auth属性,决定是否进行http标准验证。
request.onreadystatechange = function handleLoad() {
// readyState == 4说明数据接收完成
if (!request || request.readyState !== 4) {
return;
}
// request为0可能url不存在,也可能是文件传输协议,因此要区分
if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
return;
}
//getAllResponseHeaders返回的包含header的字符串,parseHeaders返回解析后的对象
var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
//根据responseType获取响应数据
var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;
var response = {
data: responseData,
status: request.status,
statusText: request.statusText,
headers: responseHeaders,
config: config,
request: request
};
//settle内部调用resolve,改变promise状态返回response
settle(resolve, reject, response);
request = null;
};
总结一下onreadystatechange的功能
- 区分url不存在还是file协议
- 解析headers
- 获取data
- 构建response对象
- settle内部调用resolve,改变promise状态
if (utils.isStandardBrowserEnv()) {
var cookies = require('./../helpers/cookies');
var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ? cookies.read(config.xsrfCookieName) : undefined;
if (xsrfValue) {
requestHeaders[config.xsrfHeaderName] = xsrfValue;
}
}
在request.send前,这里用于阻止xsrf攻击。根据xsrfName读取特定的cookie字段,请求时携带交给后端验证
总结一下xhrAdapter的作用:
- 构建请求的promise
- 设置contentType
- http 基本验证
- 构建xhr对象及其相关回调
也就是说ajax的核心操作在xhrAdapter内完成
defaultConfig内其他的配置比较简单,timeout设置允许超时的时间,xsrf相关的字段用于防止攻击。
create
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
内部调用的就是createInstance,mergeConfig会将instanceConfig的属性添加到axios.defaults上
到这基本框架就介绍完毕了,下一篇应该会写axios发起请求的流程。