创建axios对象
axios源码目录结构
创建axios对象
axios.js是axios源码的第一个文件,创建axios对象,基于Axios类型。
但是不是普通的调用构造函数,而是在Axios.prototype.request的基础上添加了很多属性,所以axios对象本身其实是一个函数,一个拥有很多属性的函数。
图解如下:
下面是axios.js源代码
'use strict'; var utils = require('./utils'); var bind = require('./helpers/bind'); var Axios = require('./core/Axios'); var mergeConfig = require('./core/mergeConfig'); var defaults = require('./defaults'); /** * Create an instance of Axios * * @param {Object} defaultConfig The default config for the instance * @return {Axios} A new instance of Axios */ function createInstance(defaultConfig) { var context = new Axios(defaultConfig); //调用Axios构造函数创建Axios新实例,传入默认设置defaultConfig var instance = bind(Axios.prototype.request, context); //bind方法返回一个绑定了指定this的方法,此处将Axios.prototype.request的this绑定为context //也就是说instance现在是一个function,并且它执行时的this是Axios传递了默认设置的新实例 // Copy axios.prototype to instance utils.extend(instance, Axios.prototype, context); //调用extend方法扩展instance,将Axios原型上的属性都复制到instance上,并且Axios原型上的方法的this都绑定为context // Copy context to instance utils.extend(instance, context); //调用extend方法继续扩展instance,将Axios传递了默认设置的新实例的属性都复制到instance上 //经过上面的操作后,instance是一个function,也就是Axios.prototype.request方法,并且它上面拥有所有Axios原型上的属性,还有传递了默认设置的Axios新实例的属性 return instance; } // Create the default instance to be exported var axios = createInstance(defaults); //调用createInstance方法创建一个新axios实例 //这个新实例其实就是Axios.prototype.request方法,它身上有Axios原型上的属性和默认设置的Axios新实例的属性 // Expose Axios class to allow class inheritance axios.Axios = Axios; //为axios添加Axios属性,暴露出Axios类 // Factory for creating new instances //axios上添加create方法,用于工厂模式创建新的axios对象,可以传入自定义的设置,自定义设置会被Axios构造函数接收 //注意,mergeConfig方法,config1上同名属性会覆盖config2上的,即axios.defaults会覆盖掉instanceConfig同名属性 axios.create = function create(instanceConfig) { return createInstance(mergeConfig(axios.defaults, instanceConfig)); }; // Expose Cancel & CancelToken //暴露Cancel,CancelToken,isCancel方法 axios.Cancel = require('./cancel/Cancel'); axios.CancelToken = require('./cancel/CancelToken'); axios.isCancel = require('./cancel/isCancel'); // Expose all/spread //暴露all和spread方法 axios.all = function all(promises) { return Promise.all(promises); }; axios.spread = require('./helpers/spread'); module.exports = axios;//导出axios对象 // Allow use of default import syntax in TypeScript //允许使用typeScript中默认导入的语法 module.exports.default = axios;
defaults默认配置
'use strict'; var utils = require('./utils'); var normalizeHeaderName = require('./helpers/normalizeHeaderName'); var DEFAULT_CONTENT_TYPE = { 'Content-Type': 'application/x-www-form-urlencoded' }; function setContentTypeIfUnset(headers, value) {//如果headers中没有设置就设置新的Content-Type if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) { headers['Content-Type'] = value; } } function getDefaultAdapter() {//获取默认适配器 var adapter; // Only Node.JS has a process variable that is of [[Class]] process //只有nodejs环境下才有process对象,是Process类的实例 if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') { // For node use HTTP adapter adapter = require('./adapters/http');//nodejs环境的请求工具 } else if (typeof XMLHttpRequest !== 'undefined') { // For browsers use XHR adapter adapter = require('./adapters/xhr');//浏览器环境的请求工具 } return adapter; } var defaults = { adapter: getDefaultAdapter(),//根据环境获取默认请求工具 transformRequest: [function transformRequest(data, headers) { //默认transformRequest配置项,在请求发送之前修改data和headers normalizeHeaderName(headers, 'Accept');//标准化Accept header名 normalizeHeaderName(headers, 'Content-Type');//标准化Content-Type header名 if (utils.isFormData(data) || utils.isArrayBuffer(data) || utils.isBuffer(data) || utils.isStream(data) || utils.isFile(data) || utils.isBlob(data) //判断data是否是FormData对象,ArrayBuffer对象,是否是Buffer,是否是Stream,是否是文件,是否是blob ) {//如果是,直接返回data return data; } if (utils.isArrayBufferView(data)) {//如果data是ArrayBuffer的视图,就返回data.buffer return data.buffer; } if (utils.isURLSearchParams(data)) {//如果data是URLSearchParams对象,设置header后返回data.toString() setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8'); return data.toString(); } if (utils.isObject(data)) {//如果data是对象,设置新的Content-Type后返回JSON.stringify(data) setContentTypeIfUnset(headers, 'application/json;charset=utf-8'); return JSON.stringify(data); } return data;//返回data }], transformResponse: [function transformResponse(data) {//默认transformResponse配置项 /*eslint no-param-reassign:0*/ if (typeof data === 'string') {//如果是data是字符串 try { data = JSON.parse(data);//尝试解析data } catch (e) { /* Ignore */ } } return data; }], /** * A timeout in milliseconds to abort a request. If set to 0 (default) a * timeout is not created. */ timeout: 0,//超时时间,默认为0,意思是不设置超时时间 xsrfCookieName: 'XSRF-TOKEN',//存下xsrf token的cookie名字 xsrfHeaderName: 'X-XSRF-TOKEN',//携带xsrf token的http header名字 maxContentLength: -1,//定义http响应主体的最大尺寸 validateStatus: function validateStatus(status) { //定义一个函数,根据响应的状态值判断promise是否应该resolve或者reject //如果validateStatus返回true,就resolve;否则,就会reject 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); }); module.exports = defaults;
utils工具函数
'use strict'; var bind = require('./helpers/bind'); var isBuffer = require('is-buffer'); /*global toString:true*/ // utils is a library of generic helper functions non-specific to axios var toString = Object.prototype.toString; /** * Determine if a value is an Array * * @param {Object} val The value to test * @returns {boolean} True if value is an Array, otherwise false */ //判断一个值是否是一个数组 function isArray(val) { return toString.call(val) === '[object Array]'; } /** * Determine if a value is an ArrayBuffer * * @param {Object} val The value to test * @returns {boolean} True if value is an ArrayBuffer, otherwise false */ //判断一个值是否是一个ArrayBuffer对象 function isArrayBuffer(val) { return toString.call(val) === '[object ArrayBuffer]'; } /** * Determine if a value is a FormData * * @param {Object} val The value to test * @returns {boolean} True if value is an FormData, otherwise false */ //判断一个值是否是FormData对象 function isFormData(val) { return (typeof FormData !== 'undefined') && (val instanceof FormData); } /** * Determine if a value is a view on an ArrayBuffer * * @param {Object} val The value to test * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false */ //判断一个值是否是一个ArrayBuffer的视图 function isArrayBufferView(val) { var result; if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) { result = ArrayBuffer.isView(val); } else { result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer); } return result; } /** * Determine if a value is a String * * @param {Object} val The value to test * @returns {boolean} True if value is a String, otherwise false */ //判断一个值是否是字符串 function isString(val) { return typeof val === 'string'; } /** * Determine if a value is a Number * * @param {Object} val The value to test * @returns {boolean} True if value is a Number, otherwise false */ //判断一个值是否是一个数字 function isNumber(val) { return typeof val === 'number'; } /** * Determine if a value is undefined * * @param {Object} val The value to test * @returns {boolean} True if the value is undefined, otherwise false */ //判断一个值是否是undefined function isUndefined(val) { return typeof val === 'undefined'; } /** * Determine if a value is an Object * * @param {Object} val The value to test * @returns {boolean} True if value is an Object, otherwise false */ //判断一个值是否是一个对象 function isObject(val) { return val !== null && typeof val === 'object'; } /** * Determine if a value is a Date * * @param {Object} val The value to test * @returns {boolean} True if value is a Date, otherwise false */ //判断一个值是否是日期对象 function isDate(val) { return toString.call(val) === '[object Date]'; } /** * Determine if a value is a File * * @param {Object} val The value to test * @returns {boolean} True if value is a File, otherwise false */ //判断一个值是否是一个文件 function isFile(val) { return toString.call(val) === '[object File]'; } /** * Determine if a value is a Blob * * @param {Object} val The value to test * @returns {boolean} True if value is a Blob, otherwise false */ //判断一个值是否是一个blob对象 function isBlob(val) { return toString.call(val) === '[object Blob]'; } /** * Determine if a value is a Function * * @param {Object} val The value to test * @returns {boolean} True if value is a Function, otherwise false */ //判断一个值是否是一个函数 function isFunction(val) { return toString.call(val) === '[object Function]'; } /** * Determine if a value is a Stream * * @param {Object} val The value to test * @returns {boolean} True if value is a Stream, otherwise false */ //判断一个值是否是Stream function isStream(val) { return isObject(val) && isFunction(val.pipe); } /** * Determine if a value is a URLSearchParams object * * @param {Object} val The value to test * @returns {boolean} True if value is a URLSearchParams object, otherwise false */ //判断一个值是否是一个URLSearchParams对象 function isURLSearchParams(val) { return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams; } /** * Trim excess whitespace off the beginning and end of a string * * @param {String} str The String to trim * @returns {String} The String freed of excess whitespace */ //去除字符串首尾的空白字符 function trim(str) { return str.replace(/^\s*/, '').replace(/\s*$/, ''); } /** * Determine if we're running in a standard browser environment * * This allows axios to run in a web worker, and react-native. * Both environments support XMLHttpRequest, but not fully standard globals. * * web workers: * typeof window -> undefined * typeof document -> undefined * * react-native: * navigator.product -> 'ReactNative' * nativescript * navigator.product -> 'NativeScript' or 'NS' */ //判断是否运行在标准浏览器环境中 //这使得axios可以运行在web worker和react-native中 function isStandardBrowserEnv() { if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' || navigator.product === 'NativeScript' || navigator.product === 'NS')) { return false; } return ( typeof window !== 'undefined' && typeof document !== 'undefined' ); } /** * Iterate over an Array or an Object invoking a function for each item. * * If `obj` is an Array callback will be called passing * the value, index, and complete array for each item. * * If 'obj' is an Object callback will be called passing * the value, key, and complete object for each property. * * @param {Object|Array} obj The object to iterate * @param {Function} fn The callback to invoke for each item */ //循环一个数组或者一个对象,为每一个元素或者属性调用function function forEach(obj, fn) { // Don't bother if no value provided if (obj === null || typeof obj === 'undefined') {//obj为空,直接返回 return; } // Force an array if not already something iterable if (typeof obj !== 'object') { //如果obj不是object类型,说明不可遍历,强制转换成一个数组 /*eslint no-param-reassign:0*/ obj = [obj]; } if (isArray(obj)) {//如果obj是数组,遍历数组,每一次循环调用一次fn,接收参数依次为value,index,array // Iterate over array values for (var i = 0, l = obj.length; i < l; i++) { fn.call(null, obj[i], i, obj); } } else {//如果obj不是数组是对象 // Iterate over object keys for (var key in obj) {//使用for in循环 if (Object.prototype.hasOwnProperty.call(obj, key)) { //只有当当前key是obj自身属性的时候才调用fn,接收参数依次为value,key,object fn.call(null, obj[key], key, obj); } } } } /** * Accepts varargs expecting each argument to be an object, then * immutably merges the properties of each object and returns result. * * When multiple objects contain the same key the later object in * the arguments list will take precedence. * * Example: * * ```js * var result = merge({foo: 123}, {foo: 456}); * console.log(result.foo); // outputs 456 * ``` * * @param {Object} obj1 Object to merge * @returns {Object} Result of all merge properties */ //接收可变参数要求每个参数都是对象类型,合并每一个对象的属性然后返回结果对象 //如果多个对象包含同样的key,参数列表里越靠后的对象的属性将会覆盖之前的值 function merge(/* obj1, obj2, obj3, ... */) { var result = {};//结果对象初始化 function assignValue(val, key) { if (typeof result[key] === 'object' && typeof val === 'object') { //如果结果对象上key对应的值是object类型并且当前key对应的value也是object类型 //递归调用merge合并对象属性 result[key] = merge(result[key], val); } else {//如果key对应值是简单值,直接赋值 result[key] = val; } } for (var i = 0, l = arguments.length; i < l; i++) { //循环参数列表,对每一个参数列表里的object再调用forEach循环它的属性,assignValue将它的属性赋到结果对象上 forEach(arguments[i], assignValue); } return result; } /** * Function equal to merge with the difference being that no reference * to original objects is kept. * * @see merge * @param {Object} obj1 Object to merge * @returns {Object} Result of all merge properties */ //深层合并,合并所有对象的属性生成新的对象,去掉所有引用类型的引用 //即完全创建一个新的对象,和旧的对象不会互相影响 function deepMerge(/* obj1, obj2, obj3, ... */) { var result = {};//结果对象 function assignValue(val, key) {//val是属性值,key是属性键 if (typeof result[key] === 'object' && typeof val === 'object') { //如果结果对象上对应key的值是object类型,并且当前属性值val也是对象类型,递归调用deepMerge继续深一层合并对象 result[key] = deepMerge(result[key], val); } else if (typeof val === 'object') { //如果当前属性值val是对象类型,递归调用deepMerge继续深一层合并对象 result[key] = deepMerge({}, val); } else { //如果当前属性值是简单值,直接赋值 result[key] = val; } } for (var i = 0, l = arguments.length; i < l; i++) { //循环参数数组,对每个obj调用forEach循环,对obj的属性调用assignValue将属性复制到结果对象上 forEach(arguments[i], assignValue); } return result; } /** * Extends object a by mutably adding to it the properties of object b. * * @param {Object} a The object to be extended * @param {Object} b The object to copy properties from * @param {Object} thisArg The object to bind function to * @return {Object} The resulting value of object a */ //将b对象的属性扩展到a对象上,返回扩展后的a对象 function extend(a, b, thisArg) { //forEach循环b对象 forEach(b, function assignValue(val, key) { if (thisArg && typeof val === 'function') { //如果当前val是function并且传递了thisArg,就将val的this绑定为thisArg,然后将此属性复制到a对象上 a[key] = bind(val, thisArg); } else {//如果不是function,直接复制到a对象上 a[key] = val; } }); return a;//返回扩展后的a对象 } module.exports = { isArray: isArray, isArrayBuffer: isArrayBuffer, isBuffer: isBuffer, isFormData: isFormData, isArrayBufferView: isArrayBufferView, isString: isString, isNumber: isNumber, isObject: isObject, isUndefined: isUndefined, isDate: isDate, isFile: isFile, isBlob: isBlob, isFunction: isFunction, isStream: isStream, isURLSearchParams: isURLSearchParams, isStandardBrowserEnv: isStandardBrowserEnv, forEach: forEach, merge: merge, deepMerge: deepMerge, extend: extend, trim: trim };
mergeConfig
将默认配置和自定义配置合并成一个配置的方法
'use strict'; var utils = require('../utils'); /** * Config-specific merge-function which creates a new config-object * by merging two configuration objects together. * * @param {Object} config1 * @param {Object} config2 * @returns {Object} New object resulting from merging config2 to config1 */ //合并两个设置对象为一个设置对象 //在生成最终结果对象的时候,都是先加入config2后加入config1的属性,config1上同名属性会覆盖config2上的 module.exports = function mergeConfig(config1, config2) { // eslint-disable-next-line no-param-reassign config2 = config2 || {}; var config = {};//结果对象 utils.forEach(['url', 'method', 'params', 'data'], function valueFromConfig2(prop) { //调用forEach循环url,method,params,data这几个设置对象中的设置项 //判断如果config2中这四个设置项不为undefined,就将其加入config结果对象中 if (typeof config2[prop] !== 'undefined') { config[prop] = config2[prop]; } }); utils.forEach(['headers', 'auth', 'proxy'], function mergeDeepProperties(prop) { //调用forEach循环headers,auth,proxy这几个设置项 if (utils.isObject(config2[prop])) { //如果config2中的对应设置项是对象,调用deepMerge深层合并config1和config2上的设置项然后加入到结果对象中 config[prop] = utils.deepMerge(config1[prop], config2[prop]); } else if (typeof config2[prop] !== 'undefined') { //如果config2上对应设置项不是object是简单类型,并且不为undefined,就加入config结果对象中 config[prop] = config2[prop]; } else if (utils.isObject(config1[prop])) { //如果config1中的对应设置项是对象,调用deepMerge深层复制config1上的设置项加入到结果对象中 config[prop] = utils.deepMerge(config1[prop]); } else if (typeof config1[prop] !== 'undefined') { //如果config1上对应设置项不是object是简单类型,并且不为undefined,就加入config结果对象中 config[prop] = config1[prop]; } }); utils.forEach([ 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer', 'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName', 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'maxContentLength', 'validateStatus', 'maxRedirects', 'httpAgent', 'httpsAgent', 'cancelToken', 'socketPath' ], function defaultToConfig2(prop) { //调用forEach循环处理其他配置项 if (typeof config2[prop] !== 'undefined') { //如果config2上对应配置项不为undefined,加入config结果对象 config[prop] = config2[prop]; } else if (typeof config1[prop] !== 'undefined') { //如果config1上对应配置项不为undefined,加入config结果对象 config[prop] = config1[prop]; } }); return config; };
bind
绑定this的方法
'use strict'; module.exports = function bind(fn, thisArg) { return function wrap() { var args = new Array(arguments.length); //创建一个args数组,长度和wrap接收的参数参数长度一样 for (var i = 0; i < args.length; i++) { //循环,往args数组中插入接收到的参数 args[i] = arguments[i]; } return fn.apply(thisArg, args); //调用apply绑定fn的this为指定thisArg,传入参数数组 }; };