axios核心源码逻辑实现(一)

1,003 阅读5分钟

axios核心源码逻辑实现(一)

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

各位小伙伴们大家好,今天我们来学习一下axios的核心源码逻辑实现。axios是目前最火热的数据请求库,相对于其他的请求库,它在语法上面也做了一些简化,容易上手,内容上通俗易懂。

目标

今天我们要实现axios发送请求的基本逻辑,具体效果如下:

image.png

打印结果为:

image.png

我们先来实现如图的逻辑业务,在下一篇章我们将讲解关于发送请求相关的具体实现

实现步骤

一 创建Axios构造函数

二 在Axios原型上挂载方法

三 声明createInstance函数在内部完成相关业务

四 调用axios函数

image.png

具体功能实现

一 创建Axios构造函数

首先我们需要创建一个axios的构造函数,它的内部要实现在其实例化身上添加两个对象,并接受传递过来的config参数,注意这个参数会贯穿从上到下的代码业务,代码如下:

 //构造函数
    function Axios(config) {
      //初始化
      this.defaults = config;//为了创建 default 默认属性
      this.intercepters = {
        request: {},
        response: {}
      }
    }

可以看到我们在其实例化对象身上添加了default属性,它的值即为config对象。我们又在其实例化对象身上添加了intercepters属性,它的值为一个对象,这个对象里又包含两个属性,request和response,其分别指向一个对象。这里的request和response与我们后面的拦截器相关内的内容有联系,在这里不多做阐述

二 在Axios原型上挂载方法

第二步我们需要在在Axios的原型上挂载一些方法,这些方法就是我们调用真正的axios请求时常会用到的方法,分别是request,get,post 代码如下:

   //原型添加相关的方法
    Axios.prototype.request = function (config) {
      console.log('发送 AJAX 请求 请求的类型为 ' + config.method);
    }
    Axios.prototype.get = function (config) {
      return this.request({ method: 'GET' });
    }
    Axios.prototype.post = function (config) {
      return this.request({ method: 'POST' });
    }

可以看到在这三个方法中我们都接受了一个config形参,它会用来最后反馈axios的执行结果

三 创建createInstance函数

这一步是很关键,也是我们今天的核心代码片段。在创建的createInstance函数中,我们要完成实例化对象以及forEach遍历添加属性等一些操作。

首先我们同样需要在createInstance函数中接收一个config参数,在函数体内,我们要创建一个Axios的实例化对象,并用context接收,这个context的作用就是为下面的一个变量提供this指向和为这个变量添加属性,如下

 //声明函数
    function createInstance(config) {
      //实例化一个对象
      let context = new Axios(config);// context.get()  context.post()  但是不能当做函数使用 context() X
      
    }

需要注意的是,此时new出来的context是一个对象,不能完成get和post的请求。

接着我们需要创建一个请求函数instance,它是createInstance函数的最终返回值,这个instance函数可以被看作就是Axios.prototype.request请求函数,但需要改变其内部指向为context,如下

  //声明函数
    function createInstance(config) {
      //实例化一个对象
      let context = new Axios(config);// context.get()  context.post()  但是不能当做函数使用 context() X
      //创建请求函数
      let instance = Axios.prototype.request.bind(context);// instance 是一个函数 并且可以 instance({})  此时 instance 不能 instance.get X
      console.dir(instance);
    }

这里调用的bind函数,返回了一个新的函数instance,bind函数内部的context也完成了新函数instance内部的this指向覆盖

然后我们需要将构造函数Axios原型上添加的几种方法依次添加给instance这个复杂数据类型。

 Object.keys(Axios.prototype).forEach(key => {
        instance[key] = Axios.prototype[key].bind(context);// this.default  this.interceptors
      });

这里解释一下,Object.key( )可以用来遍历对象里面的属性,因此这里遍历了Axios原型上添加的一些方法,并利用forEach函数依次循环为instance添加方法。

当然,我们也需要为instance函数添加属性default 与 interceptors,同样的,步骤大致与上面一样,createInstance函数完整代码如下:

    //声明函数
    function createInstance(config) {
      //实例化一个对象
      let context = new Axios(config);// context.get()  context.post()  但是不能当做函数使用 context() X
      //创建请求函数
      let instance = Axios.prototype.request.bind(context);// instance 是一个函数 并且可以 instance({})  此时 instance 不能 instance.get X
      console.dir(instance);
      //将 Axios.prototype 对象中的方法添加到instance函数对象中
      Object.keys(Axios.prototype).forEach(key => {
        instance[key] = Axios.prototype[key].bind(context);// this.default  this.interceptors
      });
      //为 instance 函数对象添加属性 default 与 interceptors
      Object.keys(context).forEach(key => {
        instance[key] = context[key];
      });
      //打印 instance函数 的身上的方法和属性
      console.dir(instance)
      return instance;
    }

打印结果如下:

image.png

可以看到instance函数身上多了一些我们刚才给它添加的属性和方法,并最终将其返回

四 调用axios函数

最后一步我们调用createInstance函数,将其执行结果赋值给新变量axios,此时这个axios就是createInstance函数的执行结果instance,我们就可以调用它身上的属性和方法了

代码如下

let axios = createInstance();
    //发送请求
    axios({method:'POST'});
    axios.get({});
    axios.post({});

执行结果如图:

image.png

总结

这节课我们完成了axios发送请求的基本逻辑,各位小伙伴们要在自己手写时多调试,多打印对象身上的属性和方法,分析什么时候会发生变化以及发生了哪些变化,在后面的文章中我们需要大量的涉及到promise函数,请各位先将promise的源码复习一遍

本节代码

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title> axios 的由来</title>
</head>

<body>
  <script>
    // console.log(axios);
    // axios();
    // axios.get();
    // axios.post();

    //构造函数
    function Axios(config) {
      //初始化
      this.defaults = config;//为了创建 default 默认属性
      this.intercepters = {
        request: {},
        response: {}
      }
    }
    //原型添加相关的方法
    Axios.prototype.request = function (config) {
      console.log('发送 AJAX 请求 请求的类型为 ' + config.method);
    }
    Axios.prototype.get = function (config) {
      return this.request({ method: 'GET' });
    }
    Axios.prototype.post = function (config) {
      return this.request({ method: 'POST' });
    }

    //声明函数
    function createInstance(config) {
      //实例化一个对象
      let context = new Axios(config);// context.get()  context.post()  但是不能当做函数使用 context() X
      //创建请求函数
      let instance = Axios.prototype.request.bind(context);// instance 是一个函数 并且可以 instance({})  此时 instance 不能 instance.get X
      //将 Axios.prototype 对象中的方法添加到instance函数对象中
      Object.keys(Axios.prototype).forEach(key => {
        instance[key] = Axios.prototype[key].bind(context);// this.default  this.interceptors
      });
      //为 instance 函数对象添加属性 default 与 interceptors
      Object.keys(context).forEach(key => {
        instance[key] = context[key];
      });
      // console.dir(instance)
      return instance;
    }

    let axios = createInstance();
    //发送请求
    axios({method:'POST'});
    axios.get({});
    axios.post({});

  </script>
</body>

</html>