这词怎么读?
官方的GitHub参考专门有人问过这个问题 #802 ,高票赞同的读法是:acks--ee--oh-ss。用中文的相似音是: 哎克 C 欧斯

如果我们去写一个网络库,需要考虑什么(只考虑web端)
1、支持哪些请求方式(get、post等)
2、请求配置(通用、自定义 eg. 超时、编码)
3、支持异步、并行
4、拦截器(请求发出前拦截、请求返回后拦截)
5、取消正在发出或未发出的请求
6、请求结果回调(正确、其他的)

先参看下axios的目录结构:

  1. ├── adapters
  2. ├── README.md
  3. ├── http.js #node环境http对象
  4. └── xhr.js #web环境http对象
  5. ├── axios.js #入口
  6. ├── cancel
  7. ├── Cancel.js #取消的构造对象类
  8. ├── CancelToken.js #取消操作的包装类
  9. └── isCancel.js #工具类
  10. ├── core
  11. ├── Axios.js #Axios实例对象
  12. ├── InterceptorManager.js #拦截器控制器
  13. ├── README.md
  14. ├── buildFullPath.js #拼接请求的url、baseURL
  15. ├── createError.js #创建异常信息类工具
  16. ├── dispatchRequest.js #默认的拦截器(分发完全请求的拦截器)
  17. ├── enhanceError.js #异常信息类实体
  18. ├── mergeConfig.js #合并配置文件(用户设置的和默认的)
  19. ├── settle.js #根据http-code值来resolve/reject状态
  20. └── transformData.js #转转请求或相应的数据的工具类
  21. ├── defaults.js #默认配置类
  22. ├── helpers/ #一些帮助方法
  23. └── utils.js #通用的工具类

其核心模块主要有两个,一个是adapters 实际请求的发出的模块,另外一个就是core里面实现了一个请求从创建到完成的整个流程控制。adapters模块是对XMLHttpRequest的包装,这里不过过道的概述(主要是对一些细节的处理)。先看一下大体的流程:
接下来,我们从源码的角度来看看一个请求是如何被发出的,下面以get请求发出到收到相应为例(axios.get()):

导包

导包时发生了什么:
image.png
创建了一个axios实例,并添加了一些方法。其关键方是在createInstance()

  1. function createInstance(defaultConfig) {
  2. // 创建一个Axios实例
  3. var context = new Axios(defaultConfig);
  4. // 为request方法bind上Axios,并方法一个包装的wrap方法:可以简单理解成创建了一个对象。之所以这样创建为了方便能 axios('https://m.zz.cn/a')这样用
  5. var instance = bind(Axios.prototype.request, context);
  6. // 把 Axios.prototype 上的方法拓展到instance上
  7. utils.extend(instance, Axios.prototype, context);
  8. // 把context中的defaults、interceptors拓展到instance实例中
  9. utils.extend(instance, context);
  10. return instance;
  11. }

导包成功后,开始调用其方法了:

  1. axios
  2. .get(URL, {
  3. params: {
  4. foo: 'bar'
  5. }
  6. })
  7. .then(handleSuccess, handleFailure2)
  8. .catch(handleFailure);

在调用get方法时是在中封装的,其内部最终会调到Axios.prototype.request指向的函数中:

  1. Axios.prototype.request = function request(config) {
  2. /*eslint no-param-reassign:0*/
  3. // Allow for axios('example/url'[, config]) a la fetch API
  4. if (typeof config === 'string') {
  5. config = arguments[1] || {};
  6. config.url = arguments[0];
  7. } else {
  8. config = config || {};
  9. }
  10. config = mergeConfig(this.defaults, config);
  11. // Set config.method
  12. if (config.method) {
  13. config.method = config.method.toLowerCase();
  14. } else if (this.defaults.method) {
  15. config.method = this.defaults.method.toLowerCase();
  16. } else {
  17. config.method = 'get';
  18. }
  19. // Hook up interceptors middleware
  20. var chain = [dispatchRequest, undefined];
  21. var promise = Promise.resolve(config);
  22. this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
  23. chain.unshift(interceptor.fulfilled, interceptor.rejected);
  24. });
  25. this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
  26. chain.push(interceptor.fulfilled, interceptor.rejected);
  27. });
  28. while (chain.length) {
  29. promise = promise.then(chain.shift(), chain.shift());
  30. }
  31. return promise;
  32. };

这里面巧妙之处创建了一个请求处理链 china,创建了Promise.resolve(),然后通过promise.then()把请求链串起来。

image.png

这个请求链中有个默认添加的重要的一环dispatchRequest,它会根据不同的平台(其实在new Axios()已经确定使用哪个adapter了)来发出网络请求,并承担了对请求前后的数据包装工作。

  1. function getDefaultAdapter() {
  2. var adapter;
  3. if (typeof XMLHttpRequest !== 'undefined') {
  4. // For browsers use XHR adapter
  5. adapter = require('./adapters/xhr');
  6. } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
  7. // For node use HTTP adapter
  8. adapter = require('./adapters/http');
  9. }
  10. return adapter;
  11. }

接下来就是对后续拦截器的调用了,最终会回调到发起请求的地方。