axios对外出口api的设计方法-kb88凯时官网登录

来自:网络
时间:2024-06-10
阅读:

本文我们将讨论 axios 对外出口 api 是如何设计的。

axios 的 2 种使用方式

当通过 npm install axios 安装完 axios 之后,就可以以下 2 种方式使用 axios。

一种是在请求时传入 axios 配置项。

// get 请求
axios.get('/user', {
    params: {
      id: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  })
  .finally(function () {
    // always executed
  });
  
// post 请求
axios.post('/user', {
    firstname: 'fred',
    lastname: 'flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

还有一种,就是通过 axios.create() 创建一个包含预配置的 axios,再进行请求。

const instance = axios.create({
  baseurl: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: {'x-custom-header': 'foobar'}
});
axios.get('/user')
axios.post('/user', {firstname: 'fred',lastname: 'flintstone'})

这样就避免了每个请求中总是重复书写相同配置的苦恼。

请求方法别名

在实际项目中使用 axios 请求时,我们通过会使用 axios.method() 的方式发送请求。这样的方法一共有 8 个:

  • 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(config)

这 8 个请求方法,底层都是基于 axios.request(config) 封装的。

其中:

  • axios(config) 是 axios.request(config) 的别名
  • axios.get、axios.delete、axios.head、axios.options(url[, config]) 类似于 axios.request({ ...config, method, url, data: config?.data })
  • axios.post、axios.put、axios.patch(url[, data[, config]]) 类似于 axios.request({ ...config, method, url, data })

了解了这些请求方法别名后,我们就来看它们的底层实现。

axios 类

axios 其实是内部 axios 类的实例。axios 类的源码位于 。

其总体实现如下:

// /v1.6.8/lib/core/axios.js
class axios {
  // 1)
  constructor(instanceconfig) {
    this.defaults = instanceconfig;
    this.interceptors = {
      request: new interceptormanager(),
      response: new interceptormanager()
    };
  }
  
  // 2)
  async request(configorurl, config) {/* ... */}
  
  // 3)
  geturi(config) {/* ... */}
}
// 3)
['delete', 'get', 'head', 'options'].foreach(function foreachmethodnodata(method) {/* ... */})
['post', 'put', 'patch'].foreach(function foreachmethodnodata(method) {/* ... */})

1):可以看到,axios 类的实现代码还是比较少的,就 2 个方法:

  • async request() 就对应前面所说的 axios.request(),是所有请求的入口地方
  • geturi() 比较少用,是用来获得完整请求路径(基于当前 axios 的默认配置和你传入的配置)

2):另外,在创建 axios 实例时,实例对象上会绑定 defaults、interceptors 属性。

  • defaults 是在创建 axios 实例传入的配置,作为默认配置用
  • interceptors 则是拦截器配置入口,如果你设置过拦截器,肯定知道它

3):最后这部分的代码,其实就是基于 axios.prototype.request 方法进行封装,分别在 axios.prototype 上添加 'delete'、'get'、'head'、'options' 和 'post'、'put'、'patch' 方法,前 4 个是一类,后 3 个是一类。

接下来,我们详细说一下。

axios.prototype.request()

这是 axios 实现核心请求逻辑的方法。

为了避免赘述,我这里给出伪代码实现。

async request(configorurl, config) {
  // 获得传入的配置
  config = getfinalconfigby(configorurl, config)
  
  // 与内部默认配置合并
  config = mergeconfig(this.defaults, config);
  
  // 拼装完整请求链
  const chain = []
    .concat(requestinterceptorchain)
    .concat(dispatchrequest)
    .concat(responseinterceptorchain)
  
  // 发起并返回请求结果
  return request(chain)
}

axios.defaults/interceptors

这是 axios 实例上的两个属性。

axios.defaults 允许你做默认配置的修改,会对所有当前 axios 实例的请求都生效。

axios.interceptors 则用来提供配置请求/响应拦截器。interceptors.request 允许你在请求前修改请求配置,interceptors.response 则允许你在请求得到响应后、交由用户处理前,提前统一对响应数据做处理。

request、response 2 个属性都是 interceptormanager 实例,提供 use()/eject()/clear()(对外) 方法和 foreach()(对内)方法。

有兴趣的想要了解拦截器实现机制的读者,可以浏览之前的 一文进行学习。

请求方法别名

再来看看请求方法别名的实现。

先看 'delete'、'get'、'head'、'options' 这 4 个方法。

// /v1.6.8/lib/core/axios.js#l193-l202
['delete', 'get', 'head', 'options'].foreach(function foreachmethodnodata(method) {
  axios.prototype[method] = function(url, config) {
    return this.request(mergeconfig(config || {}, {
      method,
      url,
      data: (config || {}).data
    }));
  };
});

实现稍稍简单。本质上就是为 axios.prototype 分别添加以上述 4 种请求方式为名的请求实现。this.request() 就是 axios.prototype.request() 方法。

以 axios.delete(url, config) 为例:'delete' 作为 config.method,url 作为 config.url,最后和传入的 config 合并成一个,交由 axios.request() 处理。

再来看看 'post'、'put'、'patch' 这 3 个方法的实现。

// /v1.6.8/lib/core/axios.js#l204-l223
['post', 'put', 'patch'].foreach(function foreachmethodwithdata(method) {
  function generatehttpmethod(isform) {
    return function httpmethod(url, data, config) {
      return this.request(mergeconfig(config || {}, {
        method,
        headers: isform ? {
          'content-type': 'multipart/form-data'
        } : {},
        url,
        data
      }));
    };
  }
  axios.prototype[method] = generatehttpmethod();
  axios.prototype[method   'form'] = generatehttpmethod(true);
});

会稍稍复杂一丢丢。因为 axios 还额外支持了 'postform'、'putform'、'patchform' 3 个方法,用于传输文件的场景。

以 axios.post(url, data, config) 为例:'post' 作为 config.method,url 作为 config.url,data 作为 config.data,最后和传入的 config 合并成一个,交由 axios.request() 处理。

以 axios.postform(url, data, config) 为例,相比 axios.post(url, data, config),多传入了一个 'content-type': 'multipart/form-data' 的请求头配置。这个请求头项会帮助后端理解要处理的请求类型。

导出 axios

以上我们介绍了 axios 类的逻辑实现。

如果直接导出 axios

不过,如果只导出 axios 类作为对外输出,那么使用方式就是下面这样。

//////
// axios.js
//////
export { default as defaults } from './defaults/index.js';
export default axios;
////// 
// app.js
//////
import axios, { defaults } from 'axios'
// 使用方式一
new axios(defaults).get('/user')
new axios(defaults).post('/user', {firstname: 'fred',lastname: 'flintstone'})
// 使用方式二
const createaxios = () {
  return new axios(defaults) 
}
const axios = createaxios()
axios.get('/user')
axios.post('/user', {firstname: 'fred',lastname: 'flintstone'})

new axios() 创建实例的方式不够优雅,而且每次使用都会有一段样板代码。

为了减少这部分的工作,axios 团队在导出时针对 axios 做了一层封装,让导出 api 更加好用。源码位于 。

// /v1.6.8/lib/axios.js#l28-l44
function createinstance(defaultconfig) {
  // 1.1)
  const context = new axios(defaultconfig);
  const instance = axios.prototype.request.bind(context);
  // 2)
  // copy axios.prototype to instance
  utils.extend(instance, axios.prototype, context, {allownkeys: true});
  // copy context to instance
  utils.extend(instance, context, null, {allownkeys: true});
  // 3)
  // factory for creating new instances
  instance.create = function create(instanceconfig) {
    return createinstance(mergeconfig(defaultconfig, instanceconfig));
  };
  
  // 1.2)
  return instance;
}
// 1)
// create the default instance to be exported
const axios = createinstance(defaults);
// this module should only have a default export
export default axios

1)、首先,导出的 axios 其实是 createinstance(defaults) 的返回值

参数 defaults 是 axios 内部默认配置信息。

import defaults from './defaults/index.js';

而 createinstance() 内部,返回的其实并不是 axios 实例,而是 axios 实例的 request() 方法。

function createinstance(defaultconfig) {
  // 返回的并不是 axios 实例
  const context = new axios(defaultconfig);
  // 而是 axios 实例的 request() 方法
  const instance = axios.prototype.request.bind(context);
  
  // ...
  
  return instance;
}

这样,我们就能以 axios(config) 方式发起请求,没必要写成 axios.request(config) 这样了。

同时值得注意的时,request() 方法内部的 this 绑定到了 axios 实例(即 context)上。这样,request() 方法内部访问的 this 时就不会有问题了。

2)、不过 instance(也就是 axios.request)上现在是没有 axios 实例上的其他方法了!

因此,接下来我们把 axios.prototype 和 context 上的属性都复制给 instance。

function createinstance(defaultconfig) {
  const context = new axios(defaultconfig);
  const instance = axios.prototype.request.bind(context);
  // 把 axios.prototype 上的属性都复制给 instance
  utils.extend(instance, axios.prototype, context, {allownkeys: true});
  // 把 context 上的属性都复制给 instance
  utils.extend(instance, context, null, {allownkeys: true});
  // ...
  
  return instance;
}

如此一来,我们就能在 instance 上调用 get()/post()/geturi() 等这些方法了。

3)另外,在来实现 axios.create()

function createinstance(defaultconfig) {
  // ...
  
  // 基于内部默认配置,在创建一个新的 axios 实例
  instance.create = function create(instanceconfig) {
    return createinstance(mergeconfig(defaultconfig, instanceconfig));
  };
  
  return instance;
}

可以看到 instance.create 手动增加了 create() 方法。

create() 方法内部其实就是递归调用 createinstance()。不同的是,instance.create() 本质上是基于导出的 axios 的基础上再多一步自定义配置合并。

总结

本文我们讲解了 axios 对外出口 api 是如何设计的。

我们首先介绍了内部 axios 类的实现,这是 axios 核心逻辑所在;其次为了让出口 api 更好使用,真正导出的 axios 其实是 axios 实例的 request() 方法,最后又在增加了 create() 方法,方便进一步得到拥有自定义配置的 axios 对象。

以上就是axios对外出口api的设计方法的详细内容,更多关于axios对外出口api的资料请关注其它相关文章!

返回顶部
顶部
网站地图