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

136 阅读5分钟

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

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

前言

大家好,欢迎来到今天的axios的源码学习。在上一个篇章中我们完成了给自定义的axios添加属性方法等功能,并最后成功完成了预期的结果。废话不多说,直接来看我们今天要完成的目标。

目标

image.png

今天我们的目标是利用自己创建的axios来发送请求获取数据

执行结果如下

image.png

实现步骤

  • 创建一个JSON文件,并开启服务器

  • 创建Axios构造函数

  • 在Axios原型上挂载request方法,在内部调用dispatchRequest函数等

  • 声明dispatchRequest函数在内部完成相关业务

  • 创建xhrAdapter函数

  • 调用axios发送请求获取结果

具体功能实现

创建一个JSON文件,并开启服务器

image.png

创建一个db.json的文件,并在内部写入如下json格式的代码,内部的文本部分可根据自己喜好修改

{
  "posts": [
    {
      "id": 1,
      "title": "印象中的爱情",
      "author": "顶不住那时间"
    },
    {
      "id": 2,
      "title": "婧月思",
      "author": "贝贝"
    }
  ],
  "comments": [
    {
      "id": 1,
      "body": "some comment",
      "postId": 1
    },
    {
      "body": "所以你倾斜",
      "postId": 2,
      "id": 2
    }
  ],
  "profile": {
    "name": "typicode"
  }
}

接着右键这个json文件打开终端,输入以下命令并运行,这样一来我们就开辟了一个本地服务器

image.png

回车

image.png

接着我们在新建的html文件中引入axios的官方文件,这样我们就调用一下axios来看看是否能访问本地的服务器

image.png

 <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.min.js"></script>

右键打开页面查看调试器打印的内容

image.png

结果显示打印成功,这就说明我们成功的开启了我们的本地服务器,并可使用。

创建Axios构造函数

在进行接下来的操作之前,我们先把刚刚引入的axios.js文件和下面调用的axiso函数删除,在这一步中我们将要创建自己的Axios构造函数。

创建Ajax构造函数的步骤和之前一样,这里不多赘述,如图

image.png

    // 创建Axios构造函数
    function Axios(config) {
      this.default = config,
        this.interceptors = {
          request: '',
          response: ''
        }
    }

挂载request方法

接着,我们要在Ajax构造函数的原型身上挂载request方法,供后面的ajax函数使用

// 挂载request函数并书写相关逻辑业务
    Axios.prototype.request = function (config) {
   
    }

在request内部,我们要先创建一个成功的promise,为后期的逻辑代码执行提供保障

image.png

可以看到,这里调用了原生Promise函数,并使用了resolve( )方法,并声明变量promise来接收,那此时变量promise一定是一个成功的promise对象。

接着我们要来创建一个chains数组,里面放入dispatchRequest函数和undefined数据类型,至于为什么要这样做,等本文结束后各位便可理解。

image.png

可以看到我们目前没有声明dispatchRequest函数,我们稍后会在外面申明一个dispatchRequest函数

接下来我们要调用promise.then( )方法执行chains数组里的变量,并最终把result返回,如下

 // 挂载request函数并书写相关逻辑业务
    Axios.prototype.request = function (config) {
      // 创建一个成功的promise
      let promise = Promise.resolve(config)

      // 创建chains数组
      let chains = [dispatchRequest, undefined]

      let result = promise.then(chains[0], chains[1])
      return result
    }

这里理解一下,由于变量promise函数是一个成功的函数,所以then会执行chains[ 0 ], 也就是dispatchRequest函数,而最终返回的result必须是一个promise对象,等价于dispatchRequest函数执行的返回的结果也必须是一个promise对象

接着我们来创建dispatchRequest函数

image.png

这个函数接收参数config,在函数体内需要返回一个函数xhrAdapter,如下

   // 创建dispatchRequest函数
    function dispatchRequest(config) {
      return xhrAdapter(config).then(res => {
        return res
      }, err => {
        return err
      })
    }

可以看到xhrAdapter这个函数调用了then方法,那也就是说xhrAdapter函数的执行结果也必须是一个promise函数,那接着我们再次在外面声明一个xhrAdapter函数,函数体内返回一个promise对象

image.png

在这个实例对象promise的内部,我们要调用原生AJAX经常会用到的XMLHttpRequest( )方法,并发送请求,如图

image.png

这里就可以看出axios发送请求的核心原理还是利用了XMLHttpRequest()上的方法

接着就来到了我们很熟悉的操作,判断xhr的状态等等,调用onreadystatechange,如下

image.png

xhrAdapter函数体内完整代码:

function xhrAdapter(config) {
      return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest()
        xhr.open(config.method, config.url)
        xhr.send()

        xhr.onreadystatechange = function (config) {
          if (xhr.readyState === 4) {
            if (xhr.status >= 200 && xhr.status < 300) {
              resolve({
                //配置对象
                config: config,
                //响应体
                data: xhr.response,
                //响应头
                headers: xhr.getAllResponseHeaders(), //字符串  parseHeaders
                // xhr 请求对象
                request: xhr,
                //响应状态码
                status: xhr.status,
                //响应状态字符串
                statusText: xhr.statusText
              });
            } else {
              //失败的状态
              reject(new Error('请求失败 失败的状态码为' + xhr.status));
            }
          }
        }
      })
    }

这里看到我们最终调用了resolve方法,在resolve函数中我们传入了一个对象,对象内部的属性名要和官方axios调用返回的结果值属性名一致,而属性值从xhr身上获取得到

这样一来,xhrAdapter函数的执行结果就是一个成功的promise对象,那再回到调用它的dispatchRequest函数,也是一个成功的promise函数,再往上回到request内部,调用的chains[ 0 ]就是一个成功的promise函数,所以then的返回值也是一个成功的promise函数,那最终的result也是一个成功的promise函数

调用axios发送请求获取结果

最后,我们利用Axios原型上的request函数,利用bind方法返回一个新的函数axios。

image.png

这里可以理解为axios只是一个躯壳,其内部实质还是request函数

接着我们调用axios函数,填入请求路径和请求方法,并用变量p来接收axios的执行结果,打印p

image.png

打印结果

image.png

可以看到p是一个成功的promise函数,函数内部也返回了相应的结果值

接着我们再对axios调用then方法,打印结果看看

image.png

打印结果:

image.png

结果显示与axios执行的结果值一致。这样一来我们就实现了预期的目标。

总结

今天我们完成了利用自己创建的axios来发送请求获取数据,其中涉及到一些promise函数的知识点,而在后面的文章中,我们要更多的去使用promise函数完成业务逻辑,因此在学习axios源码时,我们必须能对promise源码进行理解手写

本节代码

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    // 创建Axios构造函数
    function Axios(config) {
      this.default = config,
        this.interceptors = {
          request: '',
          response: ''
        }
    }

    // 挂载request函数并书写相关逻辑业务
    Axios.prototype.request = function (config) {
      // 创建一个成功的promise
      let promise = Promise.resolve(config)

      // 创建chains数组
      let chains = [dispatchRequest, undefined]

      let result = promise.then(chains[0], chains[1])
      return result
    }

    // 创建dispatchRequest函数
    function dispatchRequest(config) {
      return xhrAdapter(config).then(res => {
        return res
      }, err => {
        return err
      })
    }

    // 创建xhrAdapter函数
    function xhrAdapter(config) {
      return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest()
        xhr.open(config.method, config.url)
        xhr.send()

        xhr.onreadystatechange = function (config) {
          if (xhr.readyState === 4) {
            if (xhr.status >= 200 && xhr.status < 300) {
              resolve({
                //配置对象
                config: config,
                //响应体
                data: xhr.response,
                //响应头
                headers: xhr.getAllResponseHeaders(), //字符串  parseHeaders
                // xhr 请求对象
                request: xhr,
                //响应状态码
                status: xhr.status,
                //响应状态字符串
                statusText: xhr.statusText
              });
            } else {
              //失败的状态
              reject(new Error('请求失败 失败的状态码为' + xhr.status));
            }
          }
        }
      })
    }

    let axios = Axios.prototype.request.bind(null);
    axios({
      url: 'http://localhost:3000/posts',
      method: 'GET'
    }).then(res => {
      console.log(res)
    })


  </script>
</body>

</html>