axios源码分析

Posted 小小周虾

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了axios源码分析相关的知识,希望对你有一定的参考价值。

axios源码分析

整体思路: 按代码流程,依次下层标题

  • axios构造函数:var axios = createInstance(defaults);
    • InterceptorManager构造函数:用于收集instance.interceptors.request和instance.interceptors.request内的中间件函数
    • dispatchRequest构造函数,用于执行请求
    • xhr.js
    • http.js
    • config.adapter(): 用于适配不同情况的请求,分http和xhr形式
    • Axios构造函数:function Axios (){}
// axios.js
function createInstance (defaultConfig) {
  var context = new Axios(defaultConfig);
  // 用context去执行Axios.prototype.request,返回待执行函数
  var instance = bind(Axios.prototype.request, context);

  // Copy axios.prototype to instance,将Axios.prototype的方法,扩展到instance上
  utils.extend(instance, Axios.prototype, context);

  // Copy context to instance
  utils.extend(instance, context);
  return instance;
}
// Create the default instance to be exported
var axios = createInstance(defaults);

// Expose Axios class to allow class inheritance
axios.Axios = Axios;

// 用于自定义createInstance实例
axios.create = function create (instanceConfig) {
  // 合并default和自定义config,从而得到适配器adapter
  return createInstance(utils.merge(defaults, instanceConfig));
};
module.exports = axios;

Axios.js代码逻辑:

  • 初始化Axios,提供interceptors对象用于收集中间件函数,提供request方法
function Axios (instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}
Axios.prototype.request = function request (config) { // 开始执行请求
  ...
  ...
  // 合并自定义的config配置
  config = utils.merge(defaults, { method: 'get' }, this.defaults, config);
  // Hook up interceptors middleware
  var chain = [dispatchRequest, undefined];
  var promise = Promise.resolve(config);
  // 任务编排:往chain数组头部收集interceptors.request上通过InterceptorManager.use方法添加的中间件函数
  this.interceptors.request.forEach(function unshiftRequestInterceptors (interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
  // 任务编排: 往chain数组尾部收集interceptors.request上通过InterceptorManager.use方法添加的中间件函数
  this.interceptors.response.forEach(function pushResponseInterceptors (interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });
  // 任务调度:遍历数组,将请求参数合并后,传递给dispatchRequest执行请求
  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());  // 等待请求结果
  }
  // 返回响应结果给页面的调用.then
  return promise;
}

InterceptorManager构造函数

  • 用于收集instance.interceptors.request和instance.interceptors.request内的中间件函数
function InterceptorManager () {
  // 用于收集请求和响应中间件函数
  this.handlers = [];
}
InterceptorManager.prototype.use = function use (fulfilled, rejected) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  return this.handlers.length - 1;
};

dispatchRequest构造器

module.exports = function dispatchRequest (config) {
  ...
  ...
  var adapter = config.adapter || defaults.adapter;
  // 通过适配器方法调用请求
  return adapter(config).then(function onAdapterResolution (response) {
    throwIfCancellationRequested(config);
    // Transform response data
    response.data = transformData(
      response.data,
      response.headers,
      config.transformResponse
    );
    // console.log(response)
    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);
  });
};

adapter:适配器

  • xhr.js: 适配浏览器
module.exports = function xhrAdapter (config) {
  return new Promise(function dispatchXhrRequest (resolve, reject) {
    var requestData = config.data;
    var requestHeaders = config.headers;
    var request = new XMLHttpRequest();
    var loadEvent = 'onreadystatechange';
    var xDomain = false;
    request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true);
    // Set the request timeout in MS
    request.timeout = config.timeout;
    // Listen for ready state
    request[loadEvent] = function handleLoad () {
      // 处理响应
      settle(resolve, reject, response);
    }
    // Handle low level network errors
    request.onerror = function handleError () {
    };
    // Handle timeout
    request.ontimeout = function handleTimeout () {
    };
    // Add xsrf header---使用双重cookies防御
    // This is only done if running in a standard browser environment.
    // Specifically not if we're in a web worker, or react-native.
    if (utils.isStandardBrowserEnv()) {
      var cookies = require('
./../helpers/cookies');
      // Add xsrf header
      var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ?
        cookies.read(config.xsrfCookieName) :
        undefined;
      if (xsrfValue) {
        requestHeaders[config.xsrfHeaderName] = xsrfValue;
      }
    }
    // Send the request
    request.send(requestData);
  • http.js

总结:

  • 支持promise API
  • 提供拦截请求和响应
  • 转换请求数据和响应数据
  • 同时支持浏览器和node环境
  • 取消请求和自动转换JSON数据
  • 客户端支持防御csrf攻击
  • axios对队列chain使用
  • 提供create()方法给使用者
  • 提供适配器处理不同场景,并支持自定义适配器。


以上是关于axios源码分析的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段14——Vue的axios网络请求封装

VSCode自定义代码片段14——Vue的axios网络请求封装

axios.js 源码分析

Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段

Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段