axios由浅入深(二)源码分析以及模拟实现

axios源码分析

axios和Axios的关系?
  1. 从语法上来说axios不是Axios的实例
  2. 从功能上说axios是Axios的实例
  3. axios是Axios.request函数bind()返回的函数
  4. axios作为对象有Axios原型对象上所有方法,有Axios对象上所有属性
instance与axios的区别
  1. 相同:
    1. 都是一个能发任意请求的函数:request(config)
    2. 都有发特定请求的方法:get()/post()/put()/delete()
    3. 都有默认配置和拦截器的属性:defaults/interceptors
  2. 不同:
    1. 默认配置可能不一样
    2. instance没有axios后面添加的一些方法:create()/CancelToken()/all()
axios的运行整体流程
yes
no
axios
createInstance
axios.create
执行/别名执行
config
Axios.prototype.request
request interceptors
disptachRequest
处理参数与默认参数/transformdata
adapter
报错/cancel
axios rejected
axios fulfiled
response interceptors
请求的onResolved或onRejectd
axios的请求/响应拦截器是什么
  1. 请求拦截器:
    1. 在真正发送请求前执行的回调函数
    2. 可以对请求进行检查或配置进行特定处理
    3. 成功的回调函数,传递的默认是config
    4. 失败的回调函数,传递的默认是error
  2. 响应拦截器:
    1. 在请求得到响应后执行的回调函数
    2. 可以对响应函数进行特定处理
    3. 成功的回调函数,传递的默认是response
    4. 失败的回调函数,传递的默认是error
axios的请求/响应数据转换器是什么?
  1. 请求转换器:对请求头和请求体数据进行特定处理的函数

if(utils.isObject(data)){
​ setContentTypelfUnset(headers,‘application/json;charset=utf-8’)
​ return JSON.stringify(data)
}

  1. 响应转换器:将响应体json字符串解析为js对象或数组的函数

response.data = JSON.parse(response.data)

response的整体结构
  1. data
  2. status
  3. statusText
  4. headers
  5. config
  6. request
error的整体结构
  1. message
  2. response
  3. request
如何取消未完成的请求
  1. 当配置了cancelToken对象时,保存cancel函数
    1. 创建一个用于将来中断请求的cancelPromise
    2. 并定义了一个用于取消请求的cancel函数
    3. 将cancel函数传递出来
  2. 调用cancel()取消请求
    1. 执行cancel函数,传入错误信息message
    2. 内部会让cancelPromise变为成功,且成功的值为一个Cancel对象
    3. 在cancelPromise的成功回调中中断请求,并让发请求的promise失败,失败的reason为Cancel对象
模拟实现axios的创建过程
<script>
  // 构造函数
  function Axios(config) {
		// 初始化
  this.defaults = config //为了创建defaults默认属性
  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() 但是不能当作函数使用
    // 创建请求函数
    let instance = Axios.prototype.request.bind(context) // instance 是一个函数,并且可以instace({}) 此时instace.post 还不能使用
    // 将Axios.prototype中对象的方法添加到instance函数对象中
    Object.keys(Axios.prototype).forEach(key => {
      instance[key] = Axios.prototype[key].bind(context)
    })
    // 为instance 函数对象添加属性 defaults 与 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>

在这里插入图片描述

模拟实现axios发送请求的过程
<script>
  // 1.声明构造函数
  function Axios(config) {
  	this.config = config
	}
	Axios.prototype.request = function(config) {
		// 发送请求
    // 创建一个promise对象
    let promise = Promise.resolve(config)
    // 声明一个数组
    let chains = [dispatchRequest, undefined] // undefiend 占位
    // 调用then方法指定回调
    let result = promise.then(chains[0], chains[1])
    // 返回promise的结果
    return result
  }
	// 2.dispatchRequest函数
	function dispatchRequest(config) {
		// console.log('dispatchRequest函数')
    // 调用适配器发送请求
    return xhrAdapter(config).then(response => {
      console.log(response)
      // 响应的结果进行转换处理
      return response
    }, error => {
      console.log(error)
      throw error
    })
  }
	// 3.adapter 适配器
	function xhrAdapter(config) {
    return new Promise((resolve,reject) => {
      // 发送ajax请求
      let 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){
            // 成功的状态
            resolve({
              // 配置对象
              config: config,
              // 响应体
              data: xhr.response,
              // 响应头
              headers: xhr.getAllResponseHeaders(),
              // xhr请求对象
              request: xhr,
              // 响应状态码
              status: xhr.status,
              // 响应状态自字符串
              statusText: xhr.statusText
            })
          }else{
						// 失败的状态
            reject(new Error('请求失败'))
          }
        }
      }
    })
  }
	// 4.创建axios函数
	let axios = Axios.prototype.request.bind(null)	
	
	axios({
    method: 'GET',
    url: 'http://localhost:3000/posts'
  }).then(response => {
    console.log(response)
  })
</script>

在这里插入图片描述

模拟实现axios拦截器
<script>
  // 构造函数
  function Axios(config) {
		this.config = config
  	this.interceptors = {
			request: new InterceptorManager(),
      response: new InterceptorManager()
    }
	}
	// 发送请求
	Axios.prototype.request = function(config) {
    // 创建一个promise对象
    let promise = Promise.resolve(config)
    // 创建一个数组
    const chains = [dispatchRequest, undefined]
		// 处理拦截器
    // 请求拦截器 将请求拦截器的回调,压入到chains前面
    this.interceptors.request.handlers.forEach(item => {
			chains.unshift(item.fulfilled, item.rejected)
    })
    // 响应拦截器
    this.interceptors.response.handlers.forEach(item => {
			chains.push(item.fulfilled, item.rejected)
    })
    console.log(chains)
    // 遍历
    while(chains.length > 0){
      promise = promise.then(chains.shift(),chains.shift())
    }
    return promise
  }
	// 发送请求
	function dispatchRequest(config){
    // 返回一个promise队形
    return new Promise((resolve, reject) => {
      resolve({
        status: 200,
        statusText: 'OK'
      })
    })
  }

	
  // 创建实例
  let context = new Axios({})
  // 创建axios函数
	let axios = Axios.prototype.request.bind(context)
  // 将context属性 config interceptors 添加至axios函数对象身上
  Object.keys(context).forEach(key => {
    axios[key] = context[key]
  })

	// 拦截器管理器构造函数
	function InterceptorManager(){
    this.handlers = []
  }
	InterceptorManager.prototype.use = function(fulfilled, rejected) {
		this.handlers.push({
      fulfilled,
      rejected
    })
  }
  
  
	// 设置请求拦截器
  axios.interceptors.request.use(function one(config){
  console.log('请求拦截器一号 成功')
  return config
}, function (error) {
  console.log('请求拦截器一号 失败')
  return Promise.reject(error)
})
	// 设置请求拦截器
  axios.interceptors.request.use(function two(config){
  console.log('请求拦截器二号 成功')
  return config
}, function (error) {
  console.log('请求拦截器二号 失败')
  return Promise.reject(error)
})
// 设置响应拦截器
  axios.interceptors.response.use(function one(response){
  console.log('响应拦截器一号 成功')
  return response
}, function (error) {
  console.log('响应拦截器一号 失败')
  return Promise.reject(error)
})
// 设置响应拦截器
  axios.interceptors.response.use(function two(response){
  console.log('响应拦截器二号 成功')
  return response
}, function (error) {
  console.log('响应拦截器二号 失败')
  return Promise.reject(error)
})

</script>

在这里插入图片描述

模拟实现axios取消请求
<script>
  // 构造函数
    function Axios(config) {
        this.config = config
    }
    // 原型request方法
    Axios.prototype.request = function (config) {
        return dispatchRequest(config)
    }
    // dispatchRequest 函数
    function dispatchRequest(config) {
        return xhrAdapter(config)
    }
    // xhrAdapter
    function xhrAdapter(config) {
        // 发送ajax请求
        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) {
                        // 设置成功的状态
                        resolve({
                            status: xhr.status,
                            statusText: xhr.statusText
                        })
                    } else {
                        reject(new Error('请求失败'))
                    }
                }
            }
            // 关于取消请求的处理
            if (config.cancelToken) {
                // 对cancelToken对象身上的promise对象指定成功的回调
                config.cancelToken.promise.then(value => {
                    xhr.abort()
                    reject('请求已经被取消')
                })
            }
        })
    }
    // 创建axios函数
    const context = new Axios({})
    const axios = Axios.prototype.request.bind(context)

    // celToken 构造函数
    function CancelToken(executor) {
        // 声明一个变量
        var resolvePromise
        // 为实例对象添加属性
        this.promise = new Promise((resolve) => {
            // 将resolve赋值给resolvePromise
            resolvePromise = resolve
        })
        // 调用executor函数
        executor(function () {
            // 执行resolvePromise函数
            resolvePromise()
        })
    }

    //获取按钮
    const btns = document.querySelectorAll('button')
    // 2. 声明全局变量
    let cancel = null
    btns[0].onclick = function () {
        // 检测上一侧的请求是否完成
        if (cancel !== null) {
            // 表明上一次的请求还在继续,没有完成,所以取消上一次的请求,只执行最后一次请求
            cancel()
        }
        // 创建canceltoken的值
        let cancelToken = new CancelToken(function (c) {
            // 将c的值赋给cancel
            cancel = c
        })
        axios({
            method: 'GET',
            url: 'http:localhost:3000/posts',
            cancelToken: cancelToken
        }).then(response => {
            console.log(response)
            cancel = null
        })
    }
    btns[1].onclick = function () {
        cancel()
    }
</script>

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Y shǔ shǔ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值