前言

这应该是一个大多数都常用的请求库,因为它可以支持多种配置,跨平台实现,返回promise进行链式调用.完全过一遍源码可以提升自己对请求库的理解知识

axios源码系列(一) --- 目录结构和工具函数
axios源码系列(二) --- 适配器内部
axios源码系列(三) --- 默认配置和取消请求
axios源码系列(四) --- Axios和dispatchRequest与拦截器

默认配置

axios/lib/defaults.js

这个Axios库的默认参数配置

var utils = require('./utils');var normalizeHeaderName = require('./helpers/normalizeHeaderName');var DEFAULT_CONTENT_TYPE = {  'Content-Type': 'application/x-www-form-urlencoded'};function setContentTypeIfUnset(headers, value) {  if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {    headers['Content-Type'] = value;  }}function getDefaultAdapter() {  var adapter;  if (typeof XMLHttpRequest !== 'undefined') {    // For browsers use XHR adapter    adapter = require('./adapters/xhr');  } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {    // For node use HTTP adapter    adapter = require('./adapters/http');  }  return adapter;}

这里只有四件事:

  • 引入了工具库和解析头部名函数
  • 设置默认Content-Type为浏览器默认的编码格式
  • 定义设置headers内值为空的Content-Type对应值的方法
  • 根据宿主环境获取对应的adapters

里面解析头部名函数的源码如下

axios/lib/helpers/normalizeHeaderName.js

过滤得出是否不同大小写字母的等价请求头,是的话则设置新的请求头移除旧的请求头

var utils = require('../utils');module.exports = function normalizeHeaderName(headers, normalizedName) {  utils.forEach(headers, function processHeader(value, name) {    if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {      headers[normalizedName] = value;      delete headers[name];    }  });};

这是暴露出去的对象

var defaults = {  adapter: getDefaultAdapter(),  transformRequest: [function transformRequest(data, headers) {    normalizeHeaderName(headers, 'Accept');    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;  }],  /**   * A timeout in milliseconds to abort a request. If set to 0 (default) a   * timeout is not created.   */  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, */*'  }};utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {  defaults.headers[method] = {};});utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {  defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);});module.exports = defaults;

主要提供了几个默认配置

配置作用
adapter宿主环境对应的适配器
transformRequest内置转换函数,根据对应格式设置请求
transformResponse内置转换函数,如果是字符型则反序列化
timeout超时时间
xsrfCookieName用作 xsrf token 的值的cookie的名称
xsrfHeaderName承载 xsrf token 的值的 HTTP 头的名称
maxContentLength定义允许的响应内容的最大尺寸
validateStatus内置对于给定的HTTP 响应状态码是 resolve 或 reject promise的函数
headers默认请求头
['delete', 'get', 'head']定义相关请求方式的默认请求头{}
[post', 'put', 'patch']定义相关请求方式的默认请求头,{'Content-Type': 'application/x-www-form-urlencoded'}

取消请求

axios/lib/cancel/Cancel.js

/** * A `Cancel` is an object that is thrown when an operation is canceled. * * @class * @param {string=} message The message. */function Cancel(message) {  this.message = message;}Cancel.prototype.toString = function toString() {  return 'Cancel' + (this.message ? ': ' + this.message : '');};Cancel.prototype.__CANCEL__ = true;module.exports = Cancel;

创建Cancel的构造函数,表明取消某个请求,提供一句取消信息和标记

axios/lib/cancel/CancelToken.js

var Cancel = require('./Cancel');/** * A `CancelToken` is an object that can be used to request cancellation of an operation. * * @class * @param {Function} executor The executor function. */function CancelToken(executor) {  if (typeof executor !== 'function') {    throw new TypeError('executor must be a function.');  }  var resolvePromise;  this.promise = new Promise(function promiseExecutor(resolve) {    resolvePromise = resolve;  });  var token = this;  executor(function cancel(message) {    if (token.reason) {      // Cancellation has already been requested      return;    }    token.reason = new Cancel(message);    resolvePromise(token.reason);  });}/** * Throws a `Cancel` if cancellation has been requested. */CancelToken.prototype.throwIfRequested = function throwIfRequested() {  if (this.reason) {    throw this.reason;  }};/** * Returns an object that contains a new `CancelToken` and a function that, when called, * cancels the `CancelToken`. */CancelToken.source = function source() {  var cancel;  var token = new CancelToken(function executor(c) {    cancel = c;  });  return {    token: token,    cancel: cancel  };};module.exports = CancelToken;

创建CancelToken对象用于取消请求的操作:

  • 定义新的Promise并将改变状态的触发时机传递到外部
  • 执行传入的回调函数参数

    • 如果已经含有reason属性代表已经发出取消请求的操作,则中断操作
    • 否则创建新的Cancel对象并且改变Promise状态返回该对象

原型上绑定throwIfRequested方法,如果实例已经取消可以直接抛出取消信息

构造函数上绑定source方法,调用之后可以返回新的CancelToken实例和cancel方法

axios/lib/cancel/isCancel.js

module.exports = function isCancel(value) {  return !!(value && value.__CANCEL__);};

通过是否拥有__CANCEL__属性而得知某次请求是否已经取消了.

示例

const CancelToken = axios.CancelToken;const source = CancelToken.source();axios.get('/user/12345', {  cancelToken: source.token}).catch(function(thrown) {  if (axios.isCancel(thrown)) {    console.log('Request canceled', thrown.message);  } else {     // 处理错误  }});axios.post('/user/12345', {  name: 'new name'}, {  cancelToken: source.token})// 取消请求(message 参数是可选的)source.cancel('Operation canceled by the user.');

或者

const CancelToken = axios.CancelToken;let cancel;axios.get('/user/12345', {  cancelToken: new CancelToken(function executor(c) {    // executor 函数接收一个 cancel 函数作为参数    cancel = c;  })});// cancel the requestcancel();