源码学习01 Axios

Axios是一个基于Promise的HTTP请求库,可以用在浏览器和Node.js中。平时在Vue项目中,经常使用它来实现HTTP请求。

它的使用简便、灵活,并且有interceptors、数据转换器等强大的功能,以前用的时候并没有仔细研究过这些功能是如何实现的,正好在知乎的大前端专栏看到一篇文章对Axios的源码进行了解读。借着这篇文章的帮助,我开始了自己阅读源码的道路。

以后要多多的读源码,更多的独立完成,向大神们学习。

Axios的目录结构

Axios的目录结构相对还是比较简单的

目录里面adapters/目录下定义的是如何发出一个HTTP请求,这也就是为什么Axios技能应用在浏览器中(XHR)又能用在Node.js中(http.request),core/Axios.js是Axios的核心主类,axios.js是整个Axios的入口。

Axios的实现流程

graph TB
引入axios-->Axios构造函数实例化
Axios构造函数实例化-->Interceptors请求拦截器
Interceptors请求拦截器-->dispatchRequest方法
dispatchRequest方法-->请求转换器transformRequest
请求转换器transformRequest-->http请求适配器adapter
http请求适配器adapter-->响应转换器transformResponse
响应转换器transformResponse-->Interceptors响应拦截器

工具函数的学习

forEach

这个forEach与原生的数组的forEach并不相同,它可以遍历对象,也可以遍历数组,还可以遍历基本值:

function forEach(obj, fn) {
  // 如果是空值就返回
  if (obj === null || typeof obj === 'undefined') {
    return;
  }

  // 如果是基本类型,则放到数组里面进行遍历
  if (typeof obj !== 'object') {
    /*eslint no-param-reassign:0*/
    obj = [obj];
  }

  if (isArray(obj)) {
    // 遍历数组
    for (var i = 0, l = obj.length; i < l; i++) {
      fn.call(null, obj[i], i, obj);
    }
  } else {
    // 遍历对象
    for (var key in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, key)) {
        fn.call(null, obj[key], key, obj);
      }
    }
  }
}

mergedeepMerge

用来合并对象,二者的区别只是对于嵌套的深层的对象,deepMerge也会进行深层的拷贝,而不是指针的改变

function merge(/* obj1, obj2, obj3, ... */) {
  var result = {};
  function assignValue(val, key) {
    if (typeof result[key] === 'object' && typeof val === 'object') {
      result[key] = merge(result[key], val);
    } else {
      result[key] = val;
    }
  }

  for (var i = 0, l = arguments.length; i < l; i++) {
    forEach(arguments[i], assignValue);
  }
  return result;
}

function deepMerge(/* obj1, obj2, obj3, ... */) {
  var result = {};
  function assignValue(val, key) {
    if (typeof result[key] === 'object' && typeof val === 'object') {
      result[key] = deepMerge(result[key], val);
    } else if (typeof val === 'object') {
      result[key] = deepMerge({}, val);
    } else {
      result[key] = val;
    }
  }

  for (var i = 0, l = arguments.length; i < l; i++) {
    forEach(arguments[i], assignValue);
  }
  return result;
}

isStandardBrowserEnv

用来判断是否是标准的浏览器环境,对于Web Workers,

typeof window -> undefined
typeof document -> undefined

对于RN和NativeScript

react-native:
navigator.product -> 'ReactNative'

nativescript
navigator.product -> 'NativeScript' or 'NS'

所以有:

/**
 * Determine if we're running in a standard browser environment
 *
 * This allows axios to run in a web worker, and react-native.
 * Both environments support XMLHttpRequest, but not fully standard globals.
 *
 * web workers:
 *  typeof window -> undefined
 *  typeof document -> undefined
 *
 * react-native:
 *  navigator.product -> 'ReactNative'
 * nativescript
 *  navigator.product -> 'NativeScript' or 'NS'
 */
function isStandardBrowserEnv() {
  if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' ||
                                           navigator.product === 'NativeScript' ||
                                           navigator.product === 'NS')) {
    return false;
  }
  return (
    typeof window !== 'undefined' &&
    typeof document !== 'undefined'
  );
}

Axios的多种使用方式

Axios有多种使用方式:

import axios from 'axios';

// 第一种 axios(option)
axios({ url, method, headers });

// 第二种 axios(url[, option]);
axios(url, { method, headers })

// 第三种(对于 get/delete 等方法) axios.[method](url[, option])
axios.get(url, { headers })

// 第四种(对于 post/put等方法)axios[.method](url[, data[, option]])
axios.post(url, data, { headers })

// 第五种 axios.request(option)
axios.request({ url, method, headers })

下面从入口文件axios.js来分析这些使用方式都是如何实现的

// axios.js

// 用来创建一个 axios 的实例
function createInstance(defaultConfig) {
  // 通过默认配置新建一个 axios 实例
  var context = new Axios(defaultConfig);
  
  // 通过 bind 方法获取到 instance,并且绑定 this 上下文
  // instance 是一个函数,实际上就是 Axios.prototype.request.bind(context),其上下文指向 context
  // 所以可以通过 instance(options)的方法调用
  var instance = bind(Axios.prototype.request, context);

  // 将 Axios 原型上的属性和方法复制到 instance 上,作为静态属性和静态方法
  // Axios.prototype 上定义了 get/delete/post 等方法,所以可以直接使用 instance.get 这种形式调用
  utils.extend(instance, Axios.prototype, context);

  // 将 context 实例的属性和方法复制到 instance 上,作为静态属性和静态方法
  // context.request 指向 Axios.prototype.request,所以可以通过instance.request 这种形式调用
  utils.extend(instance, context);

  // 返回 request 方法,它与 context 的差别仅仅在于它本身是一个函数,可以直接调用
  return instance;
}

// 接受默认配置项作为参数,创建一个 request 方法,具有 Axios 的各种实例属性、方法以及原型属性和方法
// 可以认为导出的就是 Axios 的实例
var axios = createInstance(defaults);

// 暴露 Axios 类,用于继承
axios.Axios = A
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值