JS的http通信工具:axios

2020-07-29  本文已影响0人  乌云老思

基于Promise的HTTP库,用于发送Ajax,用简单的使用方法实现原生JS的xhr的功能,并且前后端皆可用

准备

安装

npm -i axios

导入

const axios = require('axios')
<script src='./axios.min.js'></script> 

基本使用

函数方式

// get请求:
let data = {
    name:"xiao"
}
let res = await axios({
    url: '/getData', // 向url发送get请求
    params: data, // 可携带参数,会自动转化为QueryString
})
console.log(res.data) // 后端传回的数据

// post请求:
let data = {
    name:"xiao"
}
let res = await axios({
    url: '/postData', // 向url发送请求
    method: 'post', // 指定psot请求
    data, // 携带数据
})
console.log(res.data) // 后端传回的数据

对象方式

// get请求:
let res = await axios.get('/getData')
console.log(res.data)

// post请求:
let data = {
    name:"xiao"
}
let res = await axios.post('/postData',data)
console.log(res.data)

高级用法

拦截器

拦截器分为:

可以拦截所有的request或response,并对其内容进行统一修饰或执行其他操作,比如:

// 1.请求拦截,以设置token为例
axios.interceptors.request.use((config) => {
    console.log(config)
    const token = localStorage.getItem("token")
    // 统一添加鉴权信息
    if(token) config.headers.authorization = token
    return config
})
// 2.响应拦截,以鉴权失败为例
axios.interceptors.response.use((res) => {
    console.log(res)
    return res
},(err) => {
    console.log(err)
    if(err.response.status==401)
    // 当出现特定错误代码,弹窗提示
        alert("please login") 
    return Promise.reject(err)
})

注意:多个请求拦截并列时逆序执行,多个响应拦截并列时顺序执行。

自定义环境切换和超时时间

自定义axios,通过axios.create()来实现:

const baseURLMap = {
    DEV: 'http://localhost:8080',
    PROD: 'http://localhost:80',
}
const currentEnv = 'DEV'
console.log(baseURLMap[currentEnv])
const myAxios = axios.create({
    // 自定义开发环境与线上环境
    baseURL: baseURLMap[currentEnv],
    // 自定义超时时间,超过此时间服务器还不响应就报错超时
    timeout: 2000, 
})

源码分析

对象调用和函数调用的实现

axios使用同一个命名就可以实现对象调用和函数调用两种功能,我们来探究一下原理。

源码@lib/axios.js

var bind = require('./helpers/bind');
var Axios = require('./core/Axios');
var defaults = require('./defaults');

function createInstance(defaultConfig) {
    // 将Axios实例化为context
  var context = new Axios(defaultConfig);
    // Axios.prototype.request是一个方法,将Axios.prototype.request和context绑定在instance中
  var instance = bind(Axios.prototype.request, context);

  // Copy axios.prototype to instance
  utils.extend(instance, Axios.prototype, context);

  // Copy context to instance
  utils.extend(instance, context);
    // 这样返回的instance就同时有方法和对象的功能
  return instance;
}

// Create the default instance to be exported
var axios = createInstance(defaults);
// 最后导出axios
module.exports = axios;

对象和方法都来自Axios类,继续看一下这个类。

源码@lib/core/Axios.js

function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

// 这里是对象功能的实现,可以发现本质上还是调用的Axios.prototype.request
// Provide aliases for supported request methods
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, config) {
    return this.request(utils.merge(config || {}, {
      method: method,
      url: url
    }));
  };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    return this.request(utils.merge(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});

module.exports = Axios;

结论:axios的对象调用和函数调用,本质上都是调用Axios.prototype.request实现的。

拦截器的实现

多次添加请求拦截器会逆序执行,而多次添加响应拦截器则是顺序执行。源码中给出了原理。

源码@lib/core/Axios.js

Axios.prototype.request = function request(config) {
  /*eslint no-param-reassign:0*/
  // Allow for axios('example/url'[, config]) a la fetch API
  if (typeof config === 'string') {
    config = arguments[1] || {};
    config.url = arguments[0];
  } else {
    config = config || {};
  }

  config = mergeConfig(this.defaults, config);

  // Set config.method
  if (config.method) {
    config.method = config.method.toLowerCase();
  } else if (this.defaults.method) {
    config.method = this.defaults.method.toLowerCase();
  } else {
    config.method = 'get';
  }

  // Hook up interceptors middleware
  var chain = [dispatchRequest, undefined];
  var promise = Promise.resolve(config);
// 请求拦截从调用链前面添加函数,链式调用时,后添加的先执行
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
// 响应拦截从调用链后面添加函数,链式调用时,先添加的先执行
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });
// 按调用链的顺序执行
  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;
};

结论:请求拦截从调用链前面添加函数,链式调用时,后添加的先执行;响应拦截从调用链后面添加函数,链式调用时,先添加的先执行。

配置优先级

配置项通过一定的规则合并,request config > instance.defaults > 系统默认,优先级高的覆盖优先级低的。

// 第一种情况,创建一个实例,系统默认配置timeout为0,在此可以设置为自定义
var instance = axios.create({
    timeout: 2000,
});

// 第二种情况,通过instance.defaults重新设置超时时间为2.5s,因为优先级比系统默认高
instance.defaults.timeout = 2500;

// 第三种情况,通过request config重新设置超时时间为5s,因为优先级比instance.defaults和系统默认都高
instance.get('/longRequest', {
  timeout: 5000
});

根据源码可以了解到:

  1. 一般情况下使用系统默认配置lib/defaults.js作为参数创建实例
  2. 而在用axios.create()创建实例时,传入两个配置参数,前者还是默认配置,后者是用户输入的配置,这时使用lib/core/mergeConfig.js合并两个配置,使第一种情况生效
  3. 通过传入配置参数创建的实例(包括系统默认配置),其配置信息储存在lib/core/Axios.jsdefaults属性中,所以可以通过直接修改instance.defaults属性来修改其配置,使第二种情况生效
  4. lib/core/Axios.jsAxios.prototype.request()中,又进行了一次自定义配置合并,可以合并临时传入的配置参数,使第三种情况生效

支持node和浏览器两种环境

源码中lib/defaults.js文件里有getDefaultAdapter()这个方法,用来判断环境。如果是浏览器就实例new XMLHttpRequest()来发送请求响应服务,node环境就引用http和https库处理和响应http服务。

源码@lib/defaults.js

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;
}
上一篇下一篇

猜你喜欢

热点阅读