专栏首页全栈者一步一步解析Axios源码,从入门到原理

一步一步解析Axios源码,从入门到原理

一. Axios是什么?


一个基于 Promise 来管理 http 请求的简洁、易用且高效的代码封装库。通俗一点来讲,它是一个前端替代Ajax的一个东西,可以使用它发起http请求接口功能,它是基于Promise的,相比于Ajax的回调函数能够更好的管理异步操作。

二. Axios的特点


三. 安装Axios


1、 通过使用npm命令安装它。

npm i axios --save

2、 安装成功后,进入的axios文件目录下,查看目录结构。

四. Axios源码分析


1、教大家怎么看NPM包的源码,第一步先看package.json,主要关注package.json中的main字段,它的值(一个相对路径)代表这个包的入口文件。这里可以看出axios的包的入口文件是index.js文件,再看一下包的scripts执行脚本,然后可以在本地执行脚本进行调试。

2、[Axios/axios.js]进入入口文件,可以看出axios的内部逻辑均在lib文件夹下。

3、[Axios/lib/axios.js]进入axios文件里,先查看export了什么内容,然后再根据导出的内容往前看。

module.exports = axios; 

4、[Axios/lib/axios.js]最终导出了axios,来看看axios是什么?

function createInstance(defaultConfig) {
  var context = new Axios(defaultConfig);
  ...
  return instance;
}

var axios = createInstance(defaults);

axios是createInstance函数传入了defaultConfig参数返回的值。

5、[Axios/lib/defaults.js]先看看这个参数defaults是什么。

// 通过nodejs中的process和浏览器的XMLHttpRequest来区别当前在前端还是nodejs中
function getDefaultAdapter() {
  var adapter;
  if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    adapter = require('./adapters/http');
  } else if (typeof XMLHttpRequest !== 'undefined') {
    adapter = require('./adapters/xhr');
  }
  return adapter;
}

var defaults = {
  adapter: getDefaultAdapter(),
  ... 
  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, */*'
  }
};

从上面便可以看出来,axios能够即在客户端使用又能在浏览器使用的奥秘,它是通过Nodejs和浏览器中各自的全局变量来区别当前在哪个环境下,然后底层各自实现,再暴露出一套统一的API出来给我们使用。同时它还默认了想 超时时间,Headers信息,alidateStatus等一些默认值进去,当我们在使用的时候不传递覆盖这些值时,即走默认的配置。

6、[Axios/adapters/http.js]先来看看Nodejs中的Axios的实现

// 进入./adapters/http,
...
var http = require('http');
var https = require('https');
...
module.exports = function httpAdapter(config) {
  return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) {
    ...
    var req = transport.request(options, function handleResponse(res) {
    ...
    // 处理response后返回
    })
    // req的error处理
    req.on('error', function handleRequestError(err) {
      ...
    });
    req.end(data);
  })
}

从上可以看出在Nodejs中,Axios的实现其实是基于nodejs的http或者http模块来发起请求的。

7、[Axios/adapters/xhr.js]再来看看在浏览器中的Axios的实现

//进入./adapters/xhr文件下
...
module.exports = function xhrAdapter(config) {
  return new Promise(function dispatchXhrRequest(resolve, reject) {
    ...
    var request = new XMLHttpRequest();
    ...
    request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true);
    ...
    request.onreadystatechange = function handleLoad() {
      ...
    };
    ...
    request.onabort = function handleAbort() {...}
    ...
    request.onerror = function handleError() {...}
    ...
    request.ontimeout = function handleTimeout() {...}
    ...
    request.send(requestData);
    ...
  })
}

一个完整的Ajax库封装流程,只不过axios暴露了一个Promise出去,所以axios在浏览器端和Ajax底层的原理是一样的,都是通过浏览器的XMLhttpRequest这个底层接口进行的一次封装。

8、[Axios/lib/core/Axios.js]上面已经看了在入口进去的axios文件中,createInstance函数传递的参数,接下来再看看createInstance内部的Axios构造函数做了什么。

// axios中
...
var context = new Axios(defaultConfig);
...

function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

Axios这个构造函数将刚才传入的defaultConfig参数挂到自己的this上,然后新增了一个interceptors拦截器对象,这个对象有request和response两个属性,接下来看一下这两个属性中的InterceptorManager 这个构造函数又是什么。

9、[Axios/lib/core/InterceptorManager.js]拦截器里的构造函数

// 进入core/InterceptorManager.js
InterceptorManager.prototype.use = function() {...}
InterceptorManager.prototype.eject =  function() {...}
InterceptorManager.prototype.forEach = function forEach(fn) {...}

拦截器暴露了三个方法use,eject,forEach三个方法,相信大家很多人在写自己的拦截器的时候都是用过use这个属性。后面两个比较少用,但是可以通过它的代码看出来,eject是删除use过的内容,forEach则是循环执行传入fn,整个拦截器部分就看完了。

10、[Axios/lib/core/Axios.js]再回到Axios.js文件里查看Axios 构造函数。

...
Axios.prototype.request = function request(config) {...})
Axios.prototype.getUri = function getUri(config) {...})
...
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, config) {
    return this.request(utils.merge(config || {}, {
      method: method,
      url: url
    }));
  };
});
...
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    return this.request(utils.merge(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});

接下来又在Axios构造函数下,挂载到原型上了9个方法,包括我们常用的下述方法。

axios.request(config)
axios.getUri(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.options(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
axios.patch(url[, data[, config]])

构造函数Axios看完了,再回到我们的入口文件axios下。

11、[Axios/lib/axios.js]回到入口文件

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;
}

首先看到一个bind方法将Axios.prototype.request和context作为参数传了进入,Axios.prototype.request是一个函数,context是一个构造出的对象,上面这一步操作就是我们可以使用Axios.get这一类静态方法的原因。

12、[Axios/lib/helper/bind.js]看看bind函数又做了什么。

module.exports = function bind(fn, thisArg) {
  return function wrap() {
    var args = new Array(arguments.length);
    for (var i = 0; i < args.length; i++) {
      args[i] = arguments[i];
    }
    return fn.apply(thisArg, args);
  };
};

bind函数在执行后返回了一个新函数,然后做了一次参数的深拷贝,接下来利用函数的apply,将传入的第二个对象参数作为一个函数参数的指定this,进行执行,即入口文件中在执行后为

Axios.prototype.request.bind(new Axios(defaultConfig);)

13、[Axios/lib/axios.js]接下来再看入口文件中剩下的两行。

  ...
  utils.extend(instance, Axios.prototype, context);
  utils.extend(instance, context);
  ...

从前面已经得出instance是一个函数,Axios.prototype和context均为两个对象。

14、[Axios/lib/utils.js]看一下utils.extend的实现。

function extend(a, b, thisArg) {
  forEach(b, function assignValue(val, key) {
    if (thisArg && typeof val === 'function') {
      a[key] = bind(val, thisArg);
    } else {
      a[key] = val;
    }
  });
  return a;
}

extend的简单大方,最后返回了第一个参数,即intance函数,并且给这个函数挂上一些静态属性。挂载的过程是先遍历了传入的Axios.prototype对象,如果该对象的值是函数就将函数绑定this后挂载在instance函数上,如果不是函数则直接挂载在构造函数上,我们使用的get,post等所有的方法均是通过这种方式进行挂载。

六. 总结与思考


整个Axios的源码流程梳理完了,可以看出它在http和浏览器底层分别实现的原理,所使用的它的方法如get,post是如何被挂载的,最常用的拦截器不单单可以被use加载,还可以通过eject删除。如果你后续需要配置一些参数却不知道如何下手,或者文档不能满足你,就请从源码层面去看看吧。

参考:http://www.axios-js.com/

如上内容均为自己总结,难免会有错误或者认识偏差,如有问题,希望大家留言指正,以免误人,若有什么问题请留言,会尽力回答之。如果对你有帮助不要忘了分享给你的朋友或者点击右下方的“在看”哦!也可以关注作者,查看历史文章并且关注最新动态,助你早日成为一名全栈工程师!

本文分享自微信公众号 - 全栈者(fullStackEngineer)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-07-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • python基础之深浅copy

    首先在了解python中的深浅拷贝之前,我们先花一点时间来了解一下python内存中变量的存储情况。对于python而言,变量的存储采用了引用语义的方式,存储的...

    天钧
  • NLPer入门指南 | 完美第一步

    你对互联网上的大量文本数据着迷吗?你是否正在寻找处理这些文本数据的方法,但不确定从哪里开始?毕竟,机器识别的是数字,而不是我们语言中的字母。在机器学习中,这可能...

    磐创AI
  • 【海康威视】 前端面经

    几个月以来,前前后后面了不少公司,当时为了找实习,很多岗位(前端,软件测试,java开发,技术支持)都面试过,最后都没有拿到offer,心累啊。。 上周突然收到...

    牛客网
  • 使用bowtie2和samblaster一步到位的干净比对

    运行速度很慢,现在有高效工具啦,比如sambamba主要有filter,merge,slice和duplicate等七个功能来处理sam/bam文件,几乎可以替...

    生信技能树
  • Lyft开源L5自动驾驶数据集:55000个人工标注的3D注释框架,还有高清空间语义地图

    这份L5数据集内容丰富,加入了原始传感摄像头和激光雷达收集到的内容,内含55000个人类标注的3D注释框架,还有高清空间语义地图。

    代码医生工作室
  • 领域驱动设计在前端中的应用

    在开始本篇文章前,我给读者们分享一个很考验人性的有趣现象,在公司洗手间的洗漱台旁边,放置了一个垃圾桶,每次我洗完手,用纸巾擦干手后,将其扔进垃圾桶,但是偶尔扔不...

    前端迷
  • Python3.7安装pyspider

    pyspider是国人binux编写的强大的网络爬虫框架,它带有强大的WebUI、脚本编辑器、任务监控器、项目管理器以及结果处理器,同时支持多种数据库后端、多种...

    不可言诉的深渊
  • 路径常用函数(第十章)

    getatime(file)与getctime(file)和getmtime(file)

    天钧
  • 你知道如何获取 vue 组件自身源码路径吗?

    D2Admin[1] 是一个开源的,前端中后台集成方案,原先是基于 vue-cli2,大概是向 vue-cli3 过渡时, 作者老李,想在页面右下角加个 Tog...

    FairyEver
  • ​eggjs实战

    swagger会扫描配置的API文档格式自动生成一份json数据,而swagger官方也提供了ui来做通常的展示,当然也支持自定义ui的。不过对后端开发者来说,...

    一粒小麦

扫码关注云+社区

领取腾讯云代金券