axios 源码解析(中) 代码结构_奔赴_前端开发者

axios现在最新的版本的是v0.19.0,本节我们来分析一下它的实现源码,首先通过 gitHub地址获取到它的源代码,地址:https://github.com/axios/axios/tree/v0.19.0

https://github.com/axios/axios/tree/v0.19.0

下载后就可以看到axios的目录结构,主目录下有一个index.js文件,该文件比较简单,内容如下:

就是去引入./lib/axios模块而已,lib目录内容如下:

大致文件说明如下:

index.js            ;入口文件
    ├lib                ;代码主目录
        ├helpers            ;定义了一些辅助函数
        ├adapters          ;原生ajax和node环境下请求的封装
        ├cancel             ;取消请求的一些封装
        ├core                ;请求派发、拦截器管理、数据转换等处理
        axios.js              ;也算是入口文件吧
        default.js           ;默认配置文件
        utils.js                ;工具函数

writer by:大沙漠 QQ:22969969

./lib/axios应该也可以说是一个入口文件,主要的分支如下:

var utils = require('./utils');
var bind = require('./helpers/bind');
var Axios = require('./core/Axios');
var mergeConfig = require('./core/mergeConfig');
var defaults = require('./defaults');                            //默认配置对象

/**/

function createInstance(defaultConfig) {                        //创建一个Axios的实例 参数为:Axios的默认配置
  var context = new Axios(defaultConfig);                //创建一个./lib/core/Axios对象,作为上下文
  var instance = bind(Axios.prototype.request, context);       //创建一个instance属性,值为bind()函数的返回值

  // Copy axios.prototype to instance
  utils.extend(instance, Axios.prototype, context);          //将Axios.prototype上的方法(delete、get、head、options、post、put、patch、request)extend到instans上,通过bind进行绑定

  // Copy context to instance
  utils.extend(instance, context);                    //将context上的两个defaults和interceptors属性保存到utils上面,这两个都是对象,这样我们就可以通过axios.defaults修改配置信息,通过axios.interceptors去设置拦截器了

  return instance;                             //返回instance方法
}

// Create the default instance to be exported
var axios = createInstance(defaults);                            //创建一个默认的实例作为输出

/**/
module.exports = axios;                                            //导出符号

// Allow use of default import syntax in TypeScript
module.exports.default = axios;                                    //默认导出符号
var utils = require('./utils');
var bind = require('./helpers/bind');
var Axios = require('./core/Axios');
var mergeConfig = require('./core/mergeConfig');
var defaults = require('./defaults');                            //默认配置对象

/**/

function createInstance(defaultConfig) {                        //创建一个Axios的实例 参数为:Axios的默认配置
  var context = new Axios(defaultConfig);                //创建一个./lib/core/Axios对象,作为上下文
  var instance = bind(Axios.prototype.request, context);       //创建一个instance属性,值为bind()函数的返回值

  // Copy axios.prototype to instance
  utils.extend(instance, Axios.prototype, context);          //将Axios.prototype上的方法(delete、get、head、options、post、put、patch、request)extend到instans上,通过bind进行绑定

  // Copy context to instance
  utils.extend(instance, context);                    //将context上的两个defaults和interceptors属性保存到utils上面,这两个都是对象,这样我们就可以通过axios.defaults修改配置信息,通过axios.interceptors去设置拦截器了

  return instance;                             //返回instance方法
}

// Create the default instance to be exported
var axios = createInstance(defaults);                            //创建一个默认的实例作为输出

/**/
module.exports = axios;                                            //导出符号

// Allow use of default import syntax in TypeScript
module.exports.default = axios;                                    //默认导出符号

var);
var);
var);
var);
var//默认配置对象/**/function//创建一个Axios的实例 参数为:Axios的默认配置varnew Axios(defaultConfig);               //创建一个./lib/core/Axios对象,作为上下文
var bind(Axios.prototype.request, context);       //创建一个instance属性,值为bind()函数的返回值

// Copy axios.prototype to instance utils.extend(instance, Axios.prototype, context);          //将Axios.prototype上的方法(delete、get、head、options、post、put、patch、request)extend到instans上,通过bind进行绑定

将Axios.prototype上的方法(delete、get、head、options、post、put、patch、request)extend到instans上,通过bind进行绑定

// Copy context to instance utils.extend(instance, context);                   //将context上的两个defaults和interceptors属性保存到utils上面,这两个都是对象,这样我们就可以通过axios.defaults修改配置信息,通过axios.interceptors去设置拦截器了

将context上的两个defaults和interceptors属性保存到utils上面,这两个都是对象,这样我们就可以通过axios.defaults修改配置信息,通过axios.interceptors去设置拦截器了

return instance;                             //返回instance方法
}

// Create the default instance to be exportedvar//创建一个默认的实例作为输出/**/
module.exports
//导出符号// Allow use of default import syntax in TypeScriptdefault//默认导出符号

createInstance会创建一个./lib/core/Axios的一个对象实例,保存到局部变量context中,然后调用bind函数,将返回值保存到instance中(这就是我们调用axios()执行ajax请求时所调用的符号),bind()是一个辅助函数,如下:

bind函数,将返回值保存到instance中(这就是我们调用axios()执行ajax请求时所调用的符号),bind()是一个辅助函数,如下:
这就是我们调用axios()执行ajax请求时所调用的符号

module.exports = function bind(fn, thisArg) {        //以thisArg为上下文,执行fn函数
  return function wrap() {
    var args = new Array(arguments.length);                //将arguments按照顺序依次保存到args里面
    for (var i = 0; i < args.length; i++) {
      args[i] = arguments[i];
    }
    return fn.apply(thisArg, args);                        //执行fn函数,参数为thisArg为上下文,args为参数
  };
};
module.exports = function bind(fn, thisArg) {        //以thisArg为上下文,执行fn函数
  return function wrap() {
    var args = new Array(arguments.length);                //将arguments按照顺序依次保存到args里面
    for (var i = 0; i < args.length; i++) {
      args[i] = arguments[i];
    }
    return fn.apply(thisArg, args);                        //执行fn函数,参数为thisArg为上下文,args为参数
  };
};

function//以thisArg为上下文,执行fn函数returnfunction wrap() {
varnew//将arguments按照顺序依次保存到args里面forvar) {
args[i]
arguments[i];
}
return//执行fn函数,参数为thisArg为上下文,args为参数 };
};

 该函数是一个高阶函数的实现,它会以参数2作为上下文,执行参数1,也就是以context为上下文,执行Axios.prototype.request函数,Axios.prototype.request就是所有异步请求的入口了

Axios.prototype.requestAxios.prototype.request就是所有异步请求的入口了

 我们看一下Axios.prototype.request的实现,如下:

我们看一下Axios.prototype.request的实现,如下:Axios.prototype.request的实现,如下:

Axios.prototype.request = function request(config) {            //派发一个请求,也是ajax请求的入口
  /*eslint no-param-reassign:0*/
  // Allow for axios('example/url'[, config]) a la fetch API
  if (typeof config === 'string') {                                   //如果config对象是个字符串,  ;例如:axios('/api/1.php').then(function(){},function(){})
    config = arguments[1] || {};                                        //则将其转换为对象
    config.url = arguments[0];
  } else {
    config = config || {};
  }

  config = mergeConfig(this.defaults, config);                          //合并默认值
  config.method = config.method ? config.method.toLowerCase() : 'get';  //ajax方法,例如:get,这里是转换为小写

  // Hook up interceptors middleware
  var chain = [dispatchRequest, undefined];                             //这个是发送ajax的异步对列
  var promise = Promise.resolve(config);                                //将config转换为Promise对象

  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) {                                                //如果chain.length存在
    promise = promise.then(chain.shift(), chain.shift());                 //则执行promise.then(),这里执行dispatchRequest函数,这样就组成了异步队列
  }

  return promise;                                                     //最后返回promise对象
};
Axios.prototype.request = function request(config) {            //派发一个请求,也是ajax请求的入口
  /*eslint no-param-reassign:0*/
  // Allow for axios('example/url'[, config]) a la fetch API
  if (typeof config === 'string') {                                   //如果config对象是个字符串,  ;例如:axios('/api/1.php').then(function(){},function(){})
    config = arguments[1] || {};                                        //则将其转换为对象
    config.url = arguments[0];
  } else {
    config = config || {};
  }

  config = mergeConfig(this.defaults, config);                          //合并默认值
  config.method = config.method ? config.method.toLowerCase() : 'get';  //ajax方法,例如:get,这里是转换为小写

  // Hook up interceptors middleware
  var chain = [dispatchRequest, undefined];                             //这个是发送ajax的异步对列
  var promise = Promise.resolve(config);                                //将config转换为Promise对象

  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) {                                                //如果chain.length存在
    promise = promise.then(chain.shift(), chain.shift());                 //则执行promise.then(),这里执行dispatchRequest函数,这样就组成了异步队列
  }

  return promise;                                                     //最后返回promise对象
};

function//派发一个请求,也是ajax请求的入口/*eslint no-param-reassign:0*/// Allow for axios(‘example/url'[, config]) a la fetch APIiftypeof//如果config对象是个字符串, ;例如:axios(‘/api/1.php’).then(function(){},function(){})//则将其转换为对象];
}
else {
config
{};
}

config this//合并默认值//ajax方法,例如:get,这里是转换为小写// Hook up interceptors middlewarevar//这个是发送ajax的异步对列var//将config转换为Promise对象thisfunction//请求拦截器的逻辑(下一节介绍) chain.unshift(interceptor.fulfilled, interceptor.rejected);
});

thisfunction//响应拦截的逻辑(下一节介绍)(下一节介绍) chain.push(interceptor.fulfilled, interceptor.rejected);
});

while//如果chain.length存在//则执行promise.then(),这里执行dispatchRequest函数,这样就组成了异步队列 }

return//最后返回promise对象

这里有一个while(chain.length){}遍历循环比较难以理解,这个设计思想很新颖,这里理解了整个axios的执行流程就能理解了,拦截器也是在这里实现的。它就是遍历chain数组,依次把前两个元素分别作为promise().then的参数1和参数2来执行,这样当promise之前的队列执行完后就会接着执行后面的队列,默认就是[dispatchRequest,undefined],也就是首先会执行dispatchRequest,如果有添加了请求拦截器则会在dispatchRequest之前执行拦截器里的逻辑,同样的,如果有响应拦截器,则会在执行dispatchRequest之后执行响应拦截器里的逻辑。

dispatchRequest逻辑如下:

module.exports = function dispatchRequest(config) {                //派发一个到服务器的请求,用config里的配置
  throwIfCancellationRequested(config);

  // Support baseURL config
  if (config.baseURL && !isAbsoluteURL(config.url)) {                //如果config.baseURL存在,且config.url不是绝对URL(以http://开头的)
    config.url = combineURLs(config.baseURL, config.url);                //则调用combineURLs将config.baseURL拼凑在config.url的前面,我们在项目里设置的baseURL="api/"就是在这里处理的
  }

  // Ensure headers exist
  config.headers = config.headers || {};                            //确保headers存在

  // Transform request data
  config.data = transformData(                                        //修改请求数据,会调用默认配置里的transformRequest进行处理
    config.data,
    config.headers,
    config.transformRequest
  );

  // Flatten headers
  config.headers = utils.merge(                                     //将请求头合并为一个数组
    config.headers.common || {},
    config.headers[config.method] || {},
    config.headers || {}
  );

  utils.forEach(                                                     //再删除config.headers里的delete、get、head、post、put、patch、common请求头
    ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
    function cleanHeaderConfig(method) {
      delete config.headers[method];
    }
  );
  //执行到这里请求头已经设置好了
  var adapter = config.adapter || defaults.adapter;                 //获取默认配置里的adapter,也就是封装好的ajax请求器

  return adapter(config).then(function onAdapterResolution(response) {    //执行adapter()就会发送ajax请求了,then()的第一个参数会修正返回的值
    throwIfCancellationRequested(config);

    // Transform response data
    response.data = transformData(                                         //调用默认配置里的transformResponse对返回的数据进行处理
      response.data,
      response.headers,
      config.transformResponse
    );

    return response;
  }, function onAdapterRejection(reason) {
    if (!isCancel(reason)) {
      throwIfCancellationRequested(config);

      // Transform response data
      if (reason && reason.response) {
        reason.response.data = transformData(
          reason.response.data,
          reason.response.headers,
          config.transformResponse
        );
      }
    }

    return Promise.reject(reason);
  });
};
module.exports = function dispatchRequest(config) {                //派发一个到服务器的请求,用config里的配置
  throwIfCancellationRequested(config);

  // Support baseURL config
  if (config.baseURL && !isAbsoluteURL(config.url)) {                //如果config.baseURL存在,且config.url不是绝对URL(以http://开头的)
    config.url = combineURLs(config.baseURL, config.url);                //则调用combineURLs将config.baseURL拼凑在config.url的前面,我们在项目里设置的baseURL="api/"就是在这里处理的
  }

  // Ensure headers exist
  config.headers = config.headers || {};                            //确保headers存在

  // Transform request data
  config.data = transformData(                                        //修改请求数据,会调用默认配置里的transformRequest进行处理
    config.data,
    config.headers,
    config.transformRequest
  );

  // Flatten headers
  config.headers = utils.merge(                                     //将请求头合并为一个数组
    config.headers.common || {},
    config.headers[config.method] || {},
    config.headers || {}
  );

  utils.forEach(                                                     //再删除config.headers里的delete、get、head、post、put、patch、common请求头
    ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
    function cleanHeaderConfig(method) {
      delete config.headers[method];
    }
  );
  //执行到这里请求头已经设置好了
  var adapter = config.adapter || defaults.adapter;                 //获取默认配置里的adapter,也就是封装好的ajax请求器

  return adapter(config).then(function onAdapterResolution(response) {    //执行adapter()就会发送ajax请求了,then()的第一个参数会修正返回的值
    throwIfCancellationRequested(config);

    // Transform response data
    response.data = transformData(                                         //调用默认配置里的transformResponse对返回的数据进行处理
      response.data,
      response.headers,
      config.transformResponse
    );

    return response;
  }, function onAdapterRejection(reason) {
    if (!isCancel(reason)) {
      throwIfCancellationRequested(config);

      // Transform response data
      if (reason && reason.response) {
        reason.response.data = transformData(
          reason.response.data,
          reason.response.headers,
          config.transformResponse
        );
      }
    }

    return Promise.reject(reason);
  });
};

function//派发一个到服务器的请求,用config里的配置 throwIfCancellationRequested(config);

// Support baseURL configif//如果config.baseURL存在,且config.url不是绝对URL(以http://开头的)//则调用combineURLs将config.baseURL拼凑在config.url的前面,我们在项目里设置的baseURL=”api/”就是在这里处理的 }

// Ensure headers exist//确保headers存在// Transform request data//修改请求数据,会调用默认配置里的transformRequest进行处理 config.data,
config.headers,
config.transformRequest
);

// Flatten headers//将请求头合并为一个数组 {},
config.headers[config.method]
{},
config.headers
{}
);

utils.forEach( //再删除config.headers里的delete、get、head、post、put、patch、common请求头],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);
//执行到这里请求头已经设置好了var//获取默认配置里的adapter,也就是封装好的ajax请求器returnfunction//执行adapter()就会发送ajax请求了,then()的第一个参数会修正返回的值 throwIfCancellationRequested(config);

// Transform response data//调用默认配置里的transformResponse对返回的数据进行处理 response.data,
response.headers,
config.transformResponse
);

return response;
},
function onAdapterRejection(reason) {
ifisCancel(reason)) {
throwIfCancellationRequested(config);

// Transform response dataif reason.response) {
reason.response.data
transformData(
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}

return Promise.reject(reason);
});
};

 最后会执行默认配置里的adapter属性对应的函数,我们来看一下,如下:

function getDefaultAdapter() {                //获取默认的适配器,就是Ajax的发送器吧
  var adapter;
  // Only node.js has a process variable that is of [[Class]] process
  if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {        //对于浏览器来说,用XHR adapter
    // For node use HTTP adapter
    adapter = require('./adapters/http');
  } else if (typeof XMLHttpRequest !== 'undefined') {                                                            //对于node环境来说,则使用HTTP adapter
    // For browsers use XHR adapter
    adapter = require('./adapters/xhr');
  }
  return adapter;
}

var defaults = {
  adapter: getDefaultAdapter(),            //适配器
  /**/
}
function getDefaultAdapter() {                //获取默认的适配器,就是Ajax的发送器吧
  var adapter;
  // Only Node.JS has a process variable that is of [[Class]] process
  if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {        //对于浏览器来说,用XHR adapter
    // For node use HTTP adapter
    adapter = require('./adapters/http');
  } else if (typeof XMLHttpRequest !== 'undefined') {                                                            //对于node环境来说,则使用HTTP adapter
    // For browsers use XHR adapter
    adapter = require('./adapters/xhr');
  }
  return adapter;
}

var defaults = {
  adapter: getDefaultAdapter(),            //适配器
  /**/
}

function//获取默认的适配器,就是Ajax的发送器吧var adapter;
// Only Node.JS has a process variable that is of [[Class]] processiftypeof//对于浏览器来说,用XHR adapter// For node use HTTP adapter);
}
elseiftypeof//对于node环境来说,则使用HTTP adapter// For browsers use XHR adapter);
}
return adapter;
}

var {
adapter: getDefaultAdapter(),
//适配器/**/
}

./adapters/http就是最终发送ajax请求的实现,主要的逻辑如下:

module.exports = function xhrAdapter(config) {                            //发送XMLHTtpRequest()请求等
  return new Promise(function dispatchXhrRequest(resolve, reject) {
    var requestData = config.data;
    var requestHeaders = config.headers;

    if (utils.isFormData(requestData)) {
      delete requestHeaders['Content-Type']; // Let the browser set it
    }

    var request = new XMLHttpRequest();

    // HTTP basic authentication
    if (config.auth) {
      var username = config.auth.username || '';
      var password = config.auth.password || '';
      requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
    }

    request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true);        //初始化HTTP请求,采用异步请求  调用buildURL获取URL地址

    // Set the request timeout in MS
    request.timeout = config.timeout;                                                    //设置超时时间

    // Listen for ready state
    request.onreadystatechange = function handleLoad() {                                //绑定onreadystatechange事件
      if (!request || request.readyState !== 4) {                                            //如果HTTP响应已经还没有接收完成
        return;                                                                                    //则直接返回,不做处理
      }

      // The request errored out and we didn't get a response, this will be
      // handled by onerror instead
      // With one exception: request that using file: protocol, most browsers
      // will return status as 0 even though it's a successful request
      if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {    //请求出错,没有得到响应的逻辑 如果request.responseURL不是以file:开头且request.status=0,则直接返回
        return;
      }

      // Prepare the response
      var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;        //解析响应头,并调用parseHeaders将其转换为对象,保存到responseHeaders里面
      var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;    //如果未设置config.responseType或者设置了responseType.responseType且等于text,则直接获取request.responseText,否则获取request.response
      var response = {                                            //拼凑返回的数据,也就是上一篇说的axios请求后返回的promise对象
        data: responseData,                                            //接收到的数据
        status: request.status,                                        //状态 ie浏览器是用1223端口代替204端口 ,见:https://github.com/axios/axios/issues/201
        statusText: request.statusText,                                //响应头的状态文字
        headers: responseHeaders,                                    //头部信息
        config: config,                                                //配置信息
        request: request                                             //对应的XmlHttpRequest对象
      };

      settle(resolve, reject, response);                        //调用settle函数进行判断,是resolve或者reject

      // Clean up request
      request = null;
    };

    /*略,主要是对于错误、超时、的一些处理*/

    // Add headers to the request
    if ('setRequestHeader' in request) {                        //如果request里面存在setRequestHeader
      utils.forEach(requestHeaders, function setRequestHeader(val, key) {                //遍历requestHeaders
        if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {    //如果key等于content-type 且没有发送数据
          // Remove Content-Type if data is undefined 
          delete requestHeaders[key];                                                             //则删除content-type这个请求头      ;只有发送数据时content-type才有用的吧
        } else {
          // Otherwise add header to the request
          request.setRequestHeader(key, val);                                                 //否则设置请求头
        }
      });
    }

    // Add withCredentials to request if needed
    if (config.withCredentials) {                                 //如果设置了跨域请求时使用凭证
      request.withCredentials = true;                                 //设置request.withCredentials为true
    }

    // Add responseType to request if needed
    if (config.responseType) {                                     //如果设置了服务器响应的数据类型,默认为json
      try {
        request.responseType = config.responseType;
      } catch (e) {
        // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.
        // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.
        if (config.responseType !== 'json') {
          throw e;
        }
      }
    }

    // Handle progress if needed
    if (typeof config.onDownloadProgress === 'function') {                     //如果设置了下载处理进度事件
      request.addEventListener('progress', config.onDownloadProgress);
    }

    // Not all browsers support upload events
    if (typeof config.onUploadProgress === 'function' && request.upload) {     //如果设置了上传处理进度事件
      request.upload.addEventListener('progress', config.onUploadProgress);
    }

    if (config.cancelToken) {
      // Handle cancellation
      config.cancelToken.promise.then(function onCanceled(cancel) {
        if (!request) {
          return;
        }

        request.abort();
        reject(cancel);
        // Clean up request
        request = null;
      });
    }

    if (requestData === undefined) {                                     //修正requestData,如果为undefined,则修正为null
      requestData = null;
    }

    // Send the request
    request.send(requestData);                                             //发送数据
  });
};
module.exports = function xhrAdapter(config) {                            //发送XMLHTtpRequest()请求等
  return new Promise(function dispatchXhrRequest(resolve, reject) {
    var requestData = config.data;
    var requestHeaders = config.headers;

    if (utils.isFormData(requestData)) {
      delete requestHeaders['Content-Type']; // Let the browser set it
    }

    var request = new XMLHttpRequest();

    // HTTP basic authentication
    if (config.auth) {
      var username = config.auth.username || '';
      var password = config.auth.password || '';
      requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
    }

    request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true);        //初始化HTTP请求,采用异步请求  调用buildURL获取URL地址

    // Set the request timeout in MS
    request.timeout = config.timeout;                                                    //设置超时时间

    // Listen for ready state
    request.onreadystatechange = function handleLoad() {                                //绑定onreadystatechange事件
      if (!request || request.readyState !== 4) {                                            //如果HTTP响应已经还没有接收完成
        return;                                                                                    //则直接返回,不做处理
      }

      // The request errored out and we didn't get a response, this will be
      // handled by onerror instead
      // With one exception: request that using file: protocol, most browsers
      // will return status as 0 even though it's a successful request
      if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {    //请求出错,没有得到响应的逻辑 如果request.responseURL不是以file:开头且request.status=0,则直接返回
        return;
      }

      // Prepare the response
      var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;        //解析响应头,并调用parseHeaders将其转换为对象,保存到responseHeaders里面
      var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;    //如果未设置config.responseType或者设置了responseType.responseType且等于text,则直接获取request.responseText,否则获取request.response
      var response = {                                            //拼凑返回的数据,也就是上一篇说的axios请求后返回的promise对象
        data: responseData,                                            //接收到的数据
        status: request.status,                                        //状态 ie浏览器是用1223端口代替204端口 ,见:https://github.com/axios/axios/issues/201
        statusText: request.statusText,                                //响应头的状态文字
        headers: responseHeaders,                                    //头部信息
        config: config,                                                //配置信息
        request: request                                             //对应的XmlHttpRequest对象
      };

      settle(resolve, reject, response);                        //调用settle函数进行判断,是resolve或者reject

      // Clean up request
      request = null;
    };

    /*略,主要是对于错误、超时、的一些处理*/

    // Add headers to the request
    if ('setRequestHeader' in request) {                        //如果request里面存在setRequestHeader
      utils.forEach(requestHeaders, function setRequestHeader(val, key) {                //遍历requestHeaders
        if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {    //如果key等于content-type 且没有发送数据
          // Remove Content-Type if data is undefined 
          delete requestHeaders[key];                                                             //则删除content-type这个请求头      ;只有发送数据时content-type才有用的吧
        } else {
          // Otherwise add header to the request
          request.setRequestHeader(key, val);                                                 //否则设置请求头
        }
      });
    }

    // Add withCredentials to request if needed
    if (config.withCredentials) {                                 //如果设置了跨域请求时使用凭证
      request.withCredentials = true;                                 //设置request.withCredentials为true
    }

    // Add responseType to request if needed
    if (config.responseType) {                                     //如果设置了服务器响应的数据类型,默认为json
      try {
        request.responseType = config.responseType;
      } catch (e) {
        // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.
        // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.
        if (config.responseType !== 'json') {
          throw e;
        }
      }
    }

    // Handle progress if needed
    if (typeof config.onDownloadProgress === 'function') {                     //如果设置了下载处理进度事件
      request.addEventListener('progress', config.onDownloadProgress);
    }

    // Not all browsers support upload events
    if (typeof config.onUploadProgress === 'function' && request.upload) {     //如果设置了上传处理进度事件
      request.upload.addEventListener('progress', config.onUploadProgress);
    }

    if (config.cancelToken) {
      // Handle cancellation
      config.cancelToken.promise.then(function onCanceled(cancel) {
        if (!request) {
          return;
        }

        request.abort();
        reject(cancel);
        // Clean up request
        request = null;
      });
    }

    if (requestData === undefined) {                                     //修正requestData,如果为undefined,则修正为null
      requestData = null;
    }

    // Send the request
    request.send(requestData);                                             //发送数据
  });
};

function//发送XMLHTtpRequest()请求等returnnewfunction dispatchXhrRequest(resolve, reject) {
var config.data;
var config.headers;

if (utils.isFormData(requestData)) {
delete// Let the browser set it }

varnew XMLHttpRequest();

// HTTP basic authenticationif (config.auth) {
var;
var;
requestHeaders.Authorization
password);
}

request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true//初始化HTTP请求,采用异步请求 调用buildURL获取URL地址// Set the request timeout in MS//设置超时时间// Listen for ready statefunction//绑定onreadystatechange事件if//如果HTTP响应已经还没有接收完成return//则直接返回,不做处理 }

// The request errored out and we didn’t get a response, this will be// handled by onerror instead// With one exception: request that using file: protocol, most browsers// will return status as 0 even though it’s a successful requestif//请求出错,没有得到响应的逻辑 如果request.responseURL不是以file:开头且request.status=0,则直接返回return;
}

// Prepare the responsevarinnull//解析响应头,并调用parseHeaders将其转换为对象,保存到responseHeaders里面var//如果未设置config.responseType或者设置了responseType.responseType且等于text,则直接获取request.responseText,否则获取request.responsevar//拼凑返回的数据,也就是上一篇说的axios请求后返回的promise对象//接收到的数据//状态 ie浏览器是用1223端口代替204端口 ,见:https://github.com/axios/axios/issues/201//响应头的状态文字//头部信息//配置信息//对应的XmlHttpRequest对象 };

settle(resolve, reject, response); //调用settle函数进行判断,是resolve或者reject// Clean up requestnull;
};

/*略,主要是对于错误、超时、的一些处理*/// Add headers to the requestifin//如果request里面存在setRequestHeaderfunction//遍历requestHeadersiftypeof//如果key等于content-type 且没有发送数据// Remove Content-Type if data is undefined delete//则删除content-type这个请求头 ;只有发送数据时content-type才有用的吧else {
// Otherwise add header to the request//否则设置请求头 }
});
}

// Add withCredentials to request if neededif//如果设置了跨域请求时使用凭证true//设置request.withCredentials为true }

// Add responseType to request if neededif//如果设置了服务器响应的数据类型,默认为jsontry {
request.responseType
config.responseType;
}
catch (e) {
// Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.// But, this can be suppressed for ‘json’ type as it can be parsed by default ‘transformResponse’ function.if) {
throw e;
}
}
}

// Handle progress if needediftypeof//如果设置了下载处理进度事件, config.onDownloadProgress);
}

// Not all browsers support upload eventsiftypeof//如果设置了上传处理进度事件, config.onUploadProgress);
}

if (config.cancelToken) {
// Handle cancellationfunction onCanceled(cancel) {
ifrequest) {
return;
}

request.abort();
reject(cancel);
// Clean up requestnull;
});
}

if//修正requestData,如果为undefined,则修正为nullnull;
}

// Send the request//发送数据 });
};

也就是原生的ajax请求了,主要的逻辑都备注了一下,这样整个流程就跑完了

对于便捷方法来说,例如axios.get()、axios.post()来说,就是对Axios.prototype.request的一次封装,实现代码如下:

utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {    //定义delete、get、head、options方法
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, config) {
    return this.request(utils.merge(config || {}, {                                             //调用utils.merge将参数合并为一个对象,然后调用request()方法
      method: method,
      url: url
    }));
  };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {              //定义post、put、patch方法
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {                                       //调用utils.merge将参数合并为一个对象,然后调用request()方法
    return this.request(utils.merge(config || {}, {
      method: method,
      url: url,
      data: data                                                                                //post、put和patch比get等请求多了个data,其它一样的
    }));
  };
});
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {    //定义delete、get、head、options方法
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, config) {
    return this.request(utils.merge(config || {}, {                                             //调用utils.merge将参数合并为一个对象,然后调用request()方法
      method: method,
      url: url
    }));
  };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {              //定义post、put、patch方法
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {                                       //调用utils.merge将参数合并为一个对象,然后调用request()方法
    return this.request(utils.merge(config || {}, {
      method: method,
      url: url,
      data: data                                                                                //post、put和patch比get等请求多了个data,其它一样的
    }));
  };
});

function//定义delete、get、head、options方法/*eslint func-names:0*/
Axios.prototype[method]
function(url, config) {
returnthis//调用utils.merge将参数合并为一个对象,然后调用request()方法 method: method,
url: url
}));
};
});

utils.forEach([function//定义post、put、patch方法/*eslint func-names:0*/
Axios.prototype[method]
function//调用utils.merge将参数合并为一个对象,然后调用request()方法returnthis {}, {
method: method,
url: url,
data: data
//post、put和patch比get等请求多了个data,其它一样的 }));
};
});

OK,搞定。

» 本文来自:前端开发者 » 《axios 源码解析(中) 代码结构_奔赴_前端开发者》
» 本文链接地址:https://www.rokub.com/73495.html
» 您也可以订阅本站:https://www.rokub.com
赞(0)
64K

评论 抢沙发

评论前必须登录!