axios源码分析简易版,如何执行?

1-axios发送请求-详解

<!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> axios 的由来</title>
  <!-- <script src="./node_modules/axios/dist/mine-axios.js"></script> -->
</head>
<body>
  <script>
    /*
      // 这个写完只能使用 axios.get来触发,还不能使用 axios({}) ????
      什么时候可以使用axios() 触发呢???

      可以触发, 默认执行的是 request 函数
    
    */


    Axios.prototype.request = function request(config) {
      console.log('request', config)
      // 预先处理config,合并
    }
    Axios.prototype.get = function(config){ return this.request({method: 'get'})}
    Axios.prototype.post = function(config){ return this.request({method: 'post'})}

    function Axios(config) {
      this.defaults = config
      this.intercepters = {
        request: {},
        response: {}
      }
    }

    function createIntance(config) {

      const context = new Axios(config)

      const intance = Axios.prototype.request.bind(context)

      // 获取Axios上的原型,挂载到axios上
      Object.keys(Axios.prototype).forEach((key) => {
        intance[key] = Axios.prototype[key].bind(context)
      })

      // 获取Axios上的属性,挂载到axios上
      Object.keys(context).forEach((key) => {
        intance[key] = context[key]
      })

      return intance
    }

    const axios = createIntance({method: 'get'})
    console.log(axios.get());
    console.log(axios({method: 'put'})); // 函数执行,this指向Axios,又因为 Axios.prototype.request使用bind向外保存了一个request函数,所以axios执行就是request函数执行

    /*
      先说axios发送请求:
      首先创建Axios的实例,定义两个属性,defaults{config}/intercepters{request/response}, 原型上定义request函数,get/post/put/delte/,在get中是通过this.request调用,来区分是否可以 axios()还是 axios.get('url')执行
        说细节在说这里:
        Axios.prototype[method] = function (url, config) {
          return this.request(
              utils.merge(
                  config || {}, 
                  {
                      method: method,
                      url: url
                  }
              )
          );
        };
      调用createInstance函数,其中调用了 new Axios,获取到this上的属性跟原型中的方法,给axios一份,所以axios = createInstance(),就可以使用Axios中的属性跟方法了

      axios跟axios.create 区别,是因为后者没有 CancelToken,Cancel,isCancel
    */
  </script>
</script>
</body>
</html>

2-axios请求发送

<!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>

    function Axios() {}

    /*
      1、处理config,自己的跟传入的合并
      2、派发请求,需要使用promise接收请求后的值,
      3、链式存储
    */
    Axios.prototype.request = function(config) {
      let promise = Promise.resolve(config)
      let chains = [dispatchRequest, undefined]
      let result = null
      
      while( chains.length ){
        result = promise.then(chains.shift(), chains.shift())
        /*
          影响原数组
          pop   后删, push    后增,影响原数组
          shift 前删, unshift 前增,影响原数组
          arr[1] = 234 影响原数组
          splice(1, 0, 2, 3)  追加数据,影响原数组

          不影响原数组。
          slice(1, 3)  >=1, <=3, 返回新数组,不影响原数组
          concat  拼接数组,返回新数组,不影响原数组
          
        */
      }
      return result
    }

    function dispatchRequest(config) {

      // 会先判断是http 还是 xhr
      // 接收到 xhr发送的请求为promise,所以用then接收
      
      const adapter = xhrAdapter(config)
      // 返回的是 成功的 promise,否则在request函数中无法使用.then接收,此时 status = resolved, data = response, 就返回给request中的result
      return adapter.then( response => {
        return response
      }, error => {
        console.log(error, '错误的请求')
      })
    }

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

        xhrHttp.send()
        // console.log(this, 'this111')

        xhrHttp.onreadystatechange = function() {
          // console.log(xhrHttp, 'xhrHttp222')
          if (xhrHttp.readyState == 4) {
            if (xhrHttp.status >= 200 && xhrHttp.status < 300) {
              const response = {
                msg: '请求成功',
                code: '200',
                data: {cardUrl: "zQfMZrUnUjAvZvQnE3zmyURn"}
              }
              resolve(response)
            } else {
              reject(new Error('请求失败 失败的状态码为' + xhrHttp.status));
            }
          }
        }
      })
    }
    const context = new Axios()
    const axios = Axios.prototype.request.bind(context)
    axios({
      url: 'http://localhost:3000/mies',
      method: 'get',
    }).then(response => {
      console.log('response', response);
    })

  /*
    1、先说axios发送请求:
      首先创建Axios的实例,定义两个属性,defaults{config}/intercepters{request/response}, 原型上定义request函数,get/post/put/delte/,在get中是通过this.request调用,来区分是否可以 axios()还是 axios.get('url')执行
        说细节在说这里:
        Axios.prototype[method] = function (url, config) {
          return this.request(
              utils.merge(
                  config || {}, 
                  {
                      method: method,
                      url: url
                  }
              )
          );
        };
      调用createInstance函数,其中调用了 new Axios,获取到this上的属性跟原型中的方法,给axios一份,所以axios = createInstance(),就可以使用Axios中的属性跟方法了

      axios跟axios.create 区别,是因为后者没有 CancelToken,Cancel,isCancel
    
    2、  请求开始发送
      function request(config) {
        const chians = [dispatch, undefined]
        let result = Promise.resolve(config)

        while(chians > 0) {
          result.promise(chians.shift(0), chians.shift(1))
        }
        return result
      }

      function dispatch() {
        return adapters.then(
          (resData) => {
            return response
          }
        )
      }
      function adapters() {
        return new Promise((resolve, reject) => {
          new XMLHttpRequest()
          send
          open
          onreadystatechange
            status === 4 && code >= 200 && code < 300
              resolve(res.data)
        })
      }
      
     2.1、调用时执行request中的dispatch,它里面返回一个 xml 的promise,xml中调用 Ajax,返回promise成功的回调,dispatch中adapter.then接收到返回的值,然后在返回给 request.promise.then,最后返回 promise,页面就可以接受到返回的 res.data      
    
    */
  </script>
</body>
</html>

3.axios拦截器实现

<!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(){
      this.default = {}
      this.interceptors = {
        request: new Interceptors(),
        response: new Interceptors()
      }
    }

    // 创建axios

    function createdInstance(config) {
      const context = new Axios(),
            instance = Axios.prototype.request.bind(context)
      Object.keys(context).forEach((key) => {
        instance[key] = context[key]
      })
      return instance
    }

    // 调用adapters中的xhr 请求
    function dispatchRequest(config){
      return new Promise((resolve, reject) => {
        resolve({
          status: '200',
          msg: '请求成功'
        })
      })
    }

    Axios.prototype.request = function(config){
     
      let promise = Promise.resolve(config)
      // this.interceptors.request = function(response) {
      //   chains.unshift(response.request, response.response)
      // }
      // this.interceptors.response = function(response) {
      //   chains.push(response.request, response.response)
      // }
      const chains = [dispatchRequest, undefined]

      this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
        debugger
          //将请求拦截器压入数组的最前面
          chains.unshift(interceptor.fulfilled, interceptor.rejected);
      });
      this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
        //将相应拦截器压入数组的最尾部
        chains.push(interceptor.fulfilled, interceptor.rejected);
      });
      while(chains.length > 0) {
        promise = promise.then(chains.shift(), chains.shift())
      }
      return promise
    }

    // 定义拦截器

    function Interceptors() {
      this.heardes = []
    }

    Interceptors.prototype.use = function(fulfilled, rejected) {
      this.heardes.push({
        fulfilled,
        rejected
      })
      console.log('this.heardes', this.heardes)
    }
    Interceptors.prototype.forEach = function(fn){
      // 简化版本
      this.heardes.forEach(item => {
        fn.call(null, item)
      })
    }




    const axios = new createdInstance()
    console.log('axios', axios)


    axios.interceptors.request.use( request => {
      console.log('我是request第一个request---use', request);
      return request
    }, reject => {
      console.log('我是request第一个reject---use');
    }
    )

    axios.interceptors.response.use(  response => {
      console.log('我是request第一个response---use', response);
      return response
    }, reject => {
      console.log('我是request第一个reject---use');
    }
    )


    axios({
      method: 'GET',
      url: 'http://XXX'
    }).then(res => {
      console.log('---<res', res);
    })


   
    /*
    1、如何获取 Fn 函数上的属性值??
    2、如果我有一个 Axios 实例,有 interceptors 属性,如何巧妙的调用 Interceptors 实例上的属性
    3、axios.interceptors.request.use 拦截器request挂载的时候需要将数据 return 出去
     3.1:因为在 while 循环执行的时候,上一个执行结果为 promise, status = resolve, data = config
       while(chains.length > 0) {
        promise = promise.then(chains.shift(), chains.shift())
      }

      3.2:使用promise.then执行这个回调函数,如果没有return, 那么 status = resolve , data = undefined
      request => {
          console.log('我是request第一个request---use', request);
          return request
        }
        
      3.3:最后axios.then 接收到的数据 就为undefined
        axios({
          method: 'GET',
          url: 'http://XXX'
        }).then(res => {
          console.log('---<res', res);
        })
     
      总结:

        function Fn(name, age) {
          this.name = name
          this.age = age
        }
        const fn = new Fn('张三', 18)
        console.log('fn', fn)
        Object.keys(fn).forEach(key => {
          console.log('key', key)
        })
      // 1、如何获取 Fn 函数上的属性值??
          可以通过 Object.keys(fn).forEach(key => {
            console.log('key', key)
          })


        
      // 2、如果我有一个 Axios 实例,有 interceptors 属性,如何巧妙的调用 Interceptors 实例上的属性
          function Axios1() {
            this.interceptors = { 
              request: new Interceptors1()
            }
          }

          function Interceptors1() {
            function fulfilled() {}
            function rejected() {}
            this.heardes = [ { fulfilled, rejected }]
          }

          Interceptors1.prototype.forEach = function (fn){
            this.heardes.forEach(item => {
              fn.call(null, item)
            })
          }

          const axios1 = new Axios1()
          function callBack(interceptor) {
            console.log('interceptor', interceptor)
          }
          axios1.interceptors.request.forEach(callBack)
      */


  /*
    1、先说axios发送请求:
      首先创建Axios的实例,定义两个属性,defaults{config}/intercepters{request/response}, 原型上定义request函数,get/post/put/delte/,在get中是通过this.request调用,来区分是否可以 axios()还是 axios.get('url')执行
        说细节在说这里:
        Axios.prototype[method] = function (url, config) {
          return this.request(
              utils.merge(
                  config || {}, 
                  {
                      method: method,
                      url: url
                  }
              )
          );
        };
      调用createInstance函数,其中调用了 new Axios,获取到this上的属性跟原型中的方法,给axios一份,所以axios = createInstance(),就可以使用Axios中的属性跟方法了

      axios跟axios.create 区别,是因为后者没有 CancelToken,Cancel,isCancel
    
    2、request 请求发送
      function request(config) {
        const chians = [dispatch, undefined]
        let result = Promise.resolve(config)

        while(chians > 0) {
          result.promise(chians.shift(0), chians.shift(1))
        }
        return result
      }

      function dispatch() {
        return adapters.then(
          (resData) => {
            return response
          }
        )
      }
      function adapters() {
        return new Promise((resolve, reject) => {
          new XMLHttpRequest()
          send
          open
          onreadystatechange
            status === 4 && code >= 200 && code < 300
              resolve(res.data)
        })
      }
      
      2。1、调用时执行request中的dispatch,它里面返回一个 xml 的promise,xml中调用 Ajax,返回promise成功的回调,dispatch中adapter.then接收到返回的值,然后在返回给 request.promise.then,最后返回 promise,页面就可以接受到返回的 res.data      
      
    3. 拦截器执行
      调用时执行request中的dispatch前先 this.Interceptors.request.use 给Interceptors中的header绑定属性,在新增一个对象,将对象中成功/失败的函数插入到 chians中,开始遍历执行,为什么拦截器之前的顺序会发生改变,因为使用unshift向前插入,执行先执行第一个,请求拦截器执行完后,config的配置发生改变,执行dispatch,在执行响应拦截器,最后返回给then,
    */
  </script>
</body>
</html>

04-axios请求取消模拟

/*
    如何理解的canToken????
    config 上配置 new cancelToken(function(cb) {
        cancel = cb
    }),
    来接收返回的一个回调,回调函数中执行 改变 xhr 中绑定promise的.then,当cancel执行,promise的状态改为 resolve,prmise的成功回调开始执行,然后request.abort()取消请求了
    

    cancelToken(cb) {
        let aResolve
        this.promise = new Promise((resolve) => {
            aResolve = resolve
        })
        cb(
            function(){ aResolve() }
        ) 
    }

    xhr 中配置的
     if (config.cancelToken) {
        config.cancelToken.promise.then(function onCanceled(cancel) {
            if (!request) {
                return;
            }
            //取消请求
            request.abort();
            reject(cancel);
        });
    }
*/
<!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>
  <!-- 模拟发送请求完成,但是需要将网络速度降低,才可以测试
    在网络中设置低速3g就可以了
  -->
  <button id="click1">我来发送请求</button>
  <button id="click2">取消发送请求</button>
  <script>
    function Axios() {}
    Axios.prototype.request = function(config) {
      debugger
      return dispatchRequest(config)
    }

    function dispatchRequest(config) {
      return adapters(config)
    }
    function adapters(config) {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()
        xhr.open(config.method, config.url)
        xhr.send()

        xhr.onreadystatechange = function() {

          if (xhr.readyState === 4) {
            if (xhr.status >= 200 && xhr.status < 300) {
              setTimeout(() => {
                resolve({
                  code: '200',
                  msg: '请求成功啦'
                })
              }, 1000)
            } else {
              reject(new Error('请求失败啦') )
            }
          }
        }
        debugger
        if (config.cancelToken) {
          config.cancelToken.promise.then(
            function tokenResolve() {
              xhr.abort()
              reject(new Error('请求取消啦'))
            },
            function cancel(){}
          )
        }
      })
    }
    const context = new Axios()
    const axios = Axios.prototype.request.bind(context)


    function CancelToken(executor) {

      let resolvePromise;
      this.promise = new Promise((resolve) => {
        resolvePromise = resolve
      })

      debugger

      executor(
        function cancel() {
          resolvePromise()
        }
      )

    }

    var cancel
    function callBack(cancelCallBack) {
      cancel = cancelCallBack
      console.log('cancelCallBack', cancelCallBack)
    }
   
    document.getElementById('click1').onclick = function () {
      axios({
        method: 'GET',
        url: 'http://localhost:3000/posts',
        cancelToken: new CancelToken(callBack)
      }).then(res => {
        cancel = null
        console.log(res, '我是请求成功的数据')
      })
    }
    document.getElementById('click2').onclick = function () {
      console.log('cancel', cancel())
    }

/*
    1、先说axios发送请求:
      首先创建Axios的实例,定义两个属性,defaults{config}/intercepters{request/response}, 原型上定义request函数,get/post/put/delte/,在get中是通过this.request调用,来区分是否可以 axios()还是 axios.get('url')执行
        说细节在说这里:
        Axios.prototype[method] = function (url, config) {
          return this.request(
              utils.merge(
                  config || {}, 
                  {
                      method: method,
                      url: url
                  }
              )
          );
        };
      调用createInstance函数,其中调用了 new Axios,获取到this上的属性跟原型中的方法,给axios一份,所以axios = createInstance(),就可以使用Axios中的属性跟方法了

      axios跟axios.create 区别,是因为后者没有 CancelToken,Cancel,isCancel
    
    2、request 请求发送
      function request(config) {
        const chians = [dispatch, undefined]
        let result = Promise.resolve(config)

        while(chians > 0) {
          result.promise(chians.shift(0), chians.shift(1))
        }
        return result
      }

      function dispatch() {
        return adapters.then(
          (resData) => {
            return response
          }
        )
      }
      function adapters() {
        return new Promise((resolve, reject) => {
          new XMLHttpRequest()
          send
          open
          onreadystatechange
            status === 4 && code >= 200 && code < 300
              resolve(res.data)
        })
      }
      
      2。1、调用时执行request中的dispatch,它里面返回一个 xml 的promise,xml中调用 Ajax,返回promise成功的回调,dispatch中adapter.then接收到返回的值,然后在返回给 request.promise.then,最后返回 promise,页面就可以接受到返回的 res.data      
      
    3. 拦截器执行
      调用时执行request中的dispatch前先 this.Interceptors.request.use 给Interceptors中的header绑定属性,在新增一个对象,将对象中成功/失败的函数插入到 chians中,开始遍历执行,为什么拦截器之前的顺序会发生改变,因为使用unshift向前插入,执行先执行第一个,请求拦截器执行完后,config的配置发生改变,执行dispatch,在执行响应拦截器,最后返回给then,
    
    4. 请求取消
      let  canFun = null
      function callback(fn) {
        canFun = fn
      }

       function CancelToken(executor) {
          let resolvePromise;
          this.promise = new Promise((resolve) => {
            resolvePromise = resolve
          })

          executor(
            function cancel() {
              resolvePromise()
            }
          )

        }

        function adaptor() {
          if (config.cancelToken) {
            config.cancelToken.promise.then(
              function tokenResolve() {
                xhr.abort()
                reject(new Error('请求取消啦'))
              },
              function cancel(){}
            )
          }
        }
      在config中添加属性,cancelToken: new cancelToken(callback)传递一个函数,new cancelToken执行接收 fn,其中有 new Promise 执行,将未执行的resolve 保留,fn函数执行后用函数包裹 resolve ,返回给外部函数,点击取消按钮执行,就会走 adaptor中then成功的回调函数,然后执行onResolve函数,会有 reject(throw '取消请求')执行,此次请求就取消了
    */
  </script>
</body>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值