前端小纠结-axios.js的实战经验

原文地址javascript

总结一下使用Axios.js的遇到的注意事项,若有不对欢迎指正。java

本文不是一篇分析源码的文章,若是须要看源码解析看这里axios实例应用及源码剖析 - xhr篇 (走心教程),写的很是不错。ios

axios.js的流程图

先放张图镇楼,后续的问题须要多看看这张图。git

来自走心教程

axios.js版本问题

以前没有注意官网说明,后来升级了版本发现报错,马上把官网的文档翻了一下,发现Semver章节有描述,github

直到v1.0以前,当发布一个minor版本,就表明有breaking changes了。因此升级有危险啊!!!typescript

拦截器interceptor

拦截器是axios.js的核心了,就是有了拦截器作解耦,才能把代码组合的更优雅。json

interceptor的返回值类型

当初在写拦截器时发现,官方给的例子,interceptor的返回值既能够是普通的value值,又能够是Promise。当时就疑惑了,有啥区别???axios

// 官方例子:
// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });
复制代码

具体细节看源码分析走心篇数组

// 源码里面就有一行代码
promise = promise.then(chain.shift(), chain.shift());
// 其实把上面例子翻译一下就是这样的
// Add a request interceptor
promise = promise.then(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
promise = promise.then(function (response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });
复制代码

是否是瞬间就明白了拦截器的return值,其实能够是普通的value值,或者Promise对象。promise

返回Promise就有不少想法了,咱们能够在拦截器interceptor中作异步操做,终于能够随心所欲了 :)。

interceptor的执行顺序

这个问题呢,是我在使用过程当中纠结的一个问题,我想作一个功能,就是当Content-Type: application/x-www-form-urlencoded;charset=utf-8时候,自动把data使用qs.stringify(后来知道这个功能其实使用transformer更合适)。

// axios/lib/core/Axios.js
  // 核心逻辑就是这段
  var chain = [dispatchRequest, undefined]; // 正中间的是发送请求的拦截器
  var promise = Promise.resolve(config);

  // request 拦截器unshift,添加到数组的头部
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
  // response 拦截器 push到数组尾部
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });

  while (chain.length) {
    // 循环执行拦截器
    promise = promise.then(chain.shift(), chain.shift());
  }
复制代码

上述代码其实就是作了两件事情:

  • 把拦截器放入数组chain中

    1. this.interceptors.request拦截器是从chain的头部放入;this.interceptors.request其实也是数组,因此request数组中最后放入的interceptor却在chain数组的最前面,和axios.interceptors.request.use时候的顺序相反。
    2. this.interceptors.response拦截器是放入chain的尾部,因此interceptor顺序和添加axios.interceptors.response.use时候的顺序相同
  • 循环执行chain中的拦截器

    request拦截器,最后添加的,最早执行

    response拦截器,按照添加的顺序执行

注意:若是你的interceptors 之间顺序有***前后依赖关系***,须要特别注意。

transformer的使用

transformRequesttransformResponse都属于Axios.js的transformer,官方宣传的Automatic transforms for JSON data 就是经过transformResponse实现的。官方文档写的太简单了,没用写要注意的问题。

内置的transformRequest和transformResponse

默认处理JSON

// axios/lib/defaults.js
var defaults = {
  adapter: getDefaultAdapter(),

  transformRequest: [function transformRequest(data, headers) {
    normalizeHeaderName(headers, 'Content-Type');
    if (utils.isFormData(data) ||
      utils.isArrayBuffer(data) ||
      utils.isBuffer(data) ||
      utils.isStream(data) ||
      utils.isFile(data) ||
      utils.isBlob(data)
    ) {
      return data;
    }
    if (utils.isArrayBufferView(data)) {
      return data.buffer;
    }
    if (utils.isURLSearchParams(data)) {
      setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
      return data.toString();
    }
    if (utils.isObject(data)) {
      setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
      return JSON.stringify(data);
    }
    return data;
  }],

  transformResponse: [function transformResponse(data) {
    /*eslint no-param-reassign:0*/
    if (typeof data === 'string') {
      try {
        data = JSON.parse(data);
      } catch (e) { /* Ignore */ }
    }
    return data;
  }],
};

复制代码
// transformRequest
if (utils.isURLSearchParams(data)) {
  setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
  return data.toString();
}
if (utils.isObject(data)) {
  setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
  return JSON.stringify(data);
}

复制代码
  • 若是data是URLSearchParams类型,axios把Content-Type修改成application/x-www-form-urlencoded;charset=utf-8,且序列化data
  • 若是data是Object,axios把Content-Type修改成application/json;charset=utf-8,且序列化data
  • transformResponse默认尝试解析JSON

内置transformRequest和transformResponse被覆盖

内置的transformRequesttransformResponse,很容易被覆盖,若是你不须要内置的那就很好办,若是你须要就须要注意下。

import axios from 'axios';
const defaults = { // 须要把默认的添加回来
  transformRequest: [].concat(axios.defaults.transformRequest, (data, headers) => {
    // do something
    return data;
  })
};

复制代码

自动序列化data参数且修改Content-Type

注意URLSearchParams兼容性问题,若是兼容IE 11,使用qs处理下。

  • 若是data是URLSearchParams类型,axios把Content-Type修改成application/x-www-form-urlencoded;charset=utf-8,且序列化data
  • 若是data是Object,axios把Content-Type修改成application/json;charset=utf-8,且序列化data

transformer和interceptor的区别

transformer和interceptor都能在请求过程当中发挥做用,有啥区别呢?

区别:

  1. transformer和interceptor的执行的时机不一样,看上图
  2. transformer主要针对data(虽然也能直接改变header,可是不建议)
  3. transformer只能同步,而interceptor能够执行异步操做
module.exports = function transformData(data, headers, fns) {
  /*eslint no-param-reassign:0*/
  utils.forEach(fns, function transform(fn) {
    data = fn(data, headers);
  });

  return data;
};

复制代码

baseUrl配置

axios内部判断config.url,若是是绝对路径开始就不添加baseUrl,不然就添加

// axios/lib/core/dispatchRequest.js
// Support baseURL config
if (config.baseURL && !isAbsoluteURL(config.url)) {
  config.url = combineURLs(config.baseURL, config.url);
}
复制代码

二进制数据注意

若是你的transformerinterceptordata有变形或者使用,必定要先判断二进制,否则容易致使报错。 推荐的作法就是,使用单独的axios实例处理二进制数据。 多个axios实例之间,注意共享headers部分(这个坑,我已经踩进去了)。

总结

以上是本人使用axios.js过程当中遇到的疑惑,而后经过查看源码的总结,若是对你有帮助,我会感到很荣幸。

axios.js相关资源

axios-retry

axios-cache-adapter

关注公众号,发现更多精彩内容。

相关文章
相关标签/搜索