乐趣区

关于前端:我司是怎么封装-axios-来处理百万级流量中平时少见过的问题

有幻想,有干货,微信搜寻 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。

本文是咱们团队每周分享的内容,该内容是由导师整顿分享的。Eaxios 是咱们前端团队本人在用的库,由导师封装的,因为其余小伙伴对它有所好奇,所以才有该篇的分享内容。

注释开始~~

Eaxios

Eaxios 是基于 axios 封装的网络申请库,在放弃 API 与 axios 基本一致的状况下,简化服务端响应内容和各种异常情况的解决。

开发背景

如上图所示,是一次 Ajax 申请可能输入的后果,在前端咱们须要依据输入后果给用户不同的提醒。

  • 申请被勾销:疏忽
  • 网络异样:提醒查看是否连贯网络
  • 申请超时:提醒网络慢,请切换网络
  • 服务器异样:提醒零碎出问题了
  • 响应解析失败:同上,且能够进行谬误日志上报
  • 申请失败:这种状况通常是业务异样,前端须要依据错误码进行相应的解决,最简略的就是音讯揭示
  • 申请胜利:前端拿到数据后更新界面

然而,现有的 Axios 库对于异样后果没有提供较好的封装,Axios Promise catch 里蕴含各种类型的谬误,而且没有提供错误码来辨认申请失败的起因。而且很多服务端接口会返回本人的错误码,这样在 Axios Promise then 里也须要解决业务异样。

此外,Axios 自身如下所述的一些问题和局限性。

  • 如果设置 Axios responseType 为 json 时,服务端返回的非 JSON 格局的响应内容会因为无奈解析,response.data 为 null

    对于 500 等谬误,响应内容会失落,所以不要去配置 responseType 为 json,对于使用者来说容易采到这个坑。

    ps:尽管 Axios 官网文档申明 responseType 是 json,实际上底层调用 XMLHttpRequest 的 responseType 是没有传值的,应该是为了躲避这个问题。

  • Axios 默认不论 HTTP 响应状态和 responseType 是什么,都会调用默认的 transformResponse

    ps:应该是为了躲避上一个问题,默认提供了一个响应处理函数进行 JSON 解析,然而这会影响性能(500 等响应内容值较多时,会造成页面卡顿)。尽管 transformResponse 能够转换 response,理论接管到的参数是 response.data,所以无奈判断具体情况来决定是否进行解析 JSON。

  • Axios then 和 catch 是依据 validateStatus 决定的,使用者解决以来较为麻烦。

    现实状况下,使用者心愿 then 返回无效的数据,catch 返回各种谬误状况:申请被勾销、网络异样、网络超时、服务端异样、服务端数据格式谬误、业务异样。

  • Axios 默认不解决 content-typeapplication/x-www-form-urlencoded 类型的申请体,应用起来不够不便

优化计划:

  • 如果设置 Axios responseType 为 json 时,不要传给传 XMLHttpRequest,以防止非 JSON 格局的响应内容失落
  • Axios 依据响应头的 content-type 判断是否须要解析 JSON,以防止性能问题

    通过申请拦截器实现不给 Axios 传递 transformResponse 配置,且将配置备份到其余字段上,而后在响应拦截器中将响应对象 response 传递给 transformResponse 解决。响应拦截器依据 response 提供的状态码、响应头和响应内容判断是否要进行 JSON 转换。

  • 勾销 Axios validateStatus 的配置选项,默认所有大于 0 的状态码都是正确的状态码,而后在 Axios 拦截器 then 中进行数据解析(非 200 的可能也是 JSON,所以要复用 200 的 JSON 解析代码),并且依据异常情况抛出直观的谬误对象
  • 内置默认解决表单类型的申请体

用法说明

eaxios 次要对响应的解决做了一些优化,除了以下局部,eaxios 的 api 与 axios 保持一致:

  • eaxios 申请配置的 transformResponse 传参和解决机会产生了变动

    axios 在服务端响应内容后就会调用 transformResponse 进行响应转换,eaxios 响应后外部主动依据响应头和 responseType 进行 JSON 解析,而后将解析后的数据和 response 传给 transformResponse,transformResponse 返回的数据最终会被 Promise resovle 给内部调用者。

    假如服务端返回的数据结构为 {code: 0, message: 'success', data: {} },code 为 0 示意正确的响应,非 0 示意异样,接口申请的代码示例如下所示:

    const eaxios = require('eaxios');
    
    eaxios.defaults.transformResponse = [function (data, response) {if (typeof data === 'object') {
          // 默认约定有胜利解析 JSON 对象,就认为服务端胜利响应,且有提供错误码
          if (data.code === 0) {return data.data;} else {throw eaxios.createError(data.message, data.code, response);
          }
        } else {
          // 50x 等服务异常情况
          throw eaxios.createError(
            data,
            response.config.responseError.SERVER_ERROR,
            response
          );
        }
      },
    ];
    
    return eaxios('https://run.mocky.io/v3/4f503449-0349-467e-a38a-c804956712b7')
      .then((data) => {console.log('success', data.id);
      })
      .catch((error) => {console.log('failure', error.code); // UNKNOWN、REQUEST_OFFLINE、REQUEST_TIMEOUT、SERVER_ERROR、RESPONSE_INVALID 和业务错误码
      });

    ps:如果存在服务单接口申请标准,能够通过 eaxios.create 创立实用于不同接口标准的申请函数。

  • eaxios 的申请处理函数 then 只会接管到 transformResponse 转换后的数据,对于网络、超时、服务端异样和业务异样等问题,会在 catch 接管一个 EaxiosError 类型的谬误对象。

    interface EaxiosError<T = any> extends Error {
      config: EaxiosRequestConfig;
      code?: string;
      request?: any;
      response?: EaxiosResponse<T>;
      isAxiosError: boolean;
      toJSON: () => object;}

    谬误处理函数能够依据错误码 code 来解决异样,code 可能的值为 UNKNOWN、REQUEST_OFFLINE、REQUEST_TIMEOUT、SERVER_ERROR、RESPONSE_INVALID 和其余业务错误码。

    ps:如果要定制错误码,能够在申请配置中增加配置项 responseError

    eaxios.defaults.responseError = {REQUEST_OFFLINE: '1'REQUEST_OFFLINE};
  • eaxios 外部会主动序列化表单类型的申请参数,所以次要传对象给 data 就行了。

代码示例

上面以 {code: 0, message: 'success', data: {} } 这样的接口标准为例,演示如何应用 eaxios。

const eaxios = require('..');

const request = eaxios.create({
  baseURL: 'https://run.mocky.io/v3',
  timeout: 30000,
  transformResponse: [function (data, response) {if (typeof data === 'object') {if (data.code === 0) {return data.data;} else {throw eaxios.createError(data.message, data.code, response);
        }
      } else {
        throw eaxios.createError(
          data,
          response.config.responseError.SERVER_ERROR,
          response,
        );
      }
    },
  ],
});

request.interceptors.response.use(function (response) {return response;},
  function (error) {if (error && error.code) {if (error.code === 'UNKNOWN') {console.log('未知谬误');
      } else if (error.code === 'REQUEST_OFFLINE') {console.log('网络未连贯');
      } else if (error.code === 'REQUEST_TIMEOUT') {console.log('网络有点慢,申请超时了');
      } else if (error.code === 'SERVER_ERROR') {console.log('零碎出问题了');
      } else if (error.code === 'RESPONSE_INVALID') {console.log('服务端 bug');
      } else if (error.code === '10000') {
        // 假如 10000 为登录会话过期
        console.log('登录会话生效');
      } else {console.log('依据状况是否要音讯提醒,还是内部解决')
      }
    }
    throw error;
  },
);

function printError(error) {
  console.log(`code: ${error.code}, name: ${error.name}, message: ${error.message}, isAxiosError: ${error.isAxiosError}, stack:\n${error.stack}`,
  );
}

function success() {console.log('>> success');
  return request('/4f503449-0349-467e-a38a-c804956712b7')
    .then((data) => {console.log('success', data);
    })
    .catch((error) => {printError(error);
    });
}

function failure() {console.log('>> failure');
  return request('/42d7c21d-5ae6-4b52-9c2d-4c3dd221eba4')
    .then((data) => {console.log('success', data);
    })
    .catch((error) => {printError(error);
    });
}

function invalid() {console.log('>> invalid');
  return request('/1b23549f-c918-4362-9ac8-35bc275c09f0')
    .then((data) => {console.log('success', data);
    })
    .catch((error) => {printError(error);
    });
}

function server_500() {console.log('>> server_500');
  return request('/2a9d8c00-9688-4d36-b2de-2dee5e81f5b3')
    .then((data) => {console.log('success', data);
    })
    .catch((error) => {printError(error);
    });
}

success().then(failure).then(invalid).then(server_500);
/* log
>> success
success {id: 1}
>> failure
登录会话生效
code: 10000, name: Error, message: error, isAxiosError: true, stack: ...
>> invalid
服务端 bug
code: RESPONSE_INVALID, name: SyntaxError, message: Unexpected token V in JSON at position 0, isAxiosError: true, stack: ...
>> server_500
零碎出问题了
code: SERVER_ERROR, name: Error, message: ...,  stack: ...
*/

兼容性

eaxios 依赖 URLSearchParams 解决表单类型的申请参数,不反对的环境须要引入响应的 polyfill

  • core-js

    require("core-js/modules/web.url-search-params.js")
  • url-search-params-polyfill

代码部署后可能存在的 BUG 没法实时晓得,预先为了解决这些 BUG,花了大量的工夫进行 log 调试,这边顺便给大家举荐一个好用的 BUG 监控工具 Fundebug。

交换

文章每周继续更新,能够微信搜寻「大迁世界」第一工夫浏览和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 曾经收录,整顿了很多我的文档,欢送 Star 和欠缺,大家面试能够参照考点温习,另外关注公众号,后盾回复 福利,即可看到福利,你懂的。

退出移动版