美文网首页
JS的http通信工具:axios

JS的http通信工具:axios

作者: 乌云老思 | 来源:发表于2020-07-29 13:39 被阅读0次

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

    准备

    安装

    npm -i axios
    

    导入

    • 后端使用时:
    const axios = require('axios')
    
    • 前端使用时:
      找到node_modules->axios->dist,将axios.min.js文件复制到static目录,然后在index.html引入
    <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;
    }
    

    相关文章

      网友评论

          本文标题:JS的http通信工具:axios

          本文链接:https://www.haomeiwen.com/subject/rqicrktx.html