模拟axios

搭建HTTP 服务

json-server网址:json-server - npm

axios 对象创建模拟实现

axios既可以当做函数直接调用,也可以当做对象调用内部函数 – 先生成函数,再往函数原型上加方法

	// 构造函数
	 function Axios(config){
	     // 初始化
	     this.defaults = config, //为了创建defaults的默认属性
	     // 拦截器
	     this.intercepters = {
	         request: {},
	         response: {}
	     }
	 }
	 /**
	  * 原型添加相关的方法
	  * axios 请求最终都是通过调用request方法去请求XMLHttpRequest
	 */
	 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) {
	     /**
	      * 实例化一个对象
	      * 可以context.get() context.post(), 但是不能当做函数使用 context()
	     */
	     let context = new Axios(config);
	     /**
	      * 创建请求函数
	      * instance 可以当做一个函数调用 instance(),
	      * 此时instance.get(), instance.post() 不能用
	     */
	     let instance = Axios.prototype.request.bind(context); 
	     // 将 Axios.prototype 对象中的方法添加到 instance 函数对象中
	     Object.keys(Axios.prototype).forEach(key => {
	         instance[key] = Axios.prototype[key].bind(context); // 让方法始终指向 context 实例
	     })
	     Object.keys(context).forEach(key => {
	         instance[key] = context[key]
	     })
	     return instance
	 }
	
	 let axios = createInstance()
	 axios({method: 'get'})
	 axios.get()
	 axios.post()

模拟axios实现发送请求

axios 是基于 promise 的http客户端,可在浏览器与node.js 服务器上发送请求
axios 是对 XMLHttpRequest的封装
axios 请求过程: axios 调用 request ,request 再调用 dispatch(适配器),dispatch 再调用 xhr。再逐步往回返回结果xhr -> dispatch -> request -> axios

	 // axios 发送请求, axios Axios.prototype.request bind
	 //1. 构造函数
	  function Axios(config){
	     this.config = config
	  }
	  Axios.prototype.request = function(config) {
	      // 创建一个永远为成功的promise
	      let promise = Promise.resolve(config)
	      // 生命一个数组 undefined 占位
	      let chains = [dispatchRequest, undefined]
	      // 调用 then 方法指定回调
	      let result = promise.then(chains[0], chains[1])
	      // 返回result 的 promise 结果
	      return result
	  }
	  //2.dispatchRequest函数
	  function dispatchRequest(config) {
	      // 调用适配器发送请求
	      return xhrAdapter(config).then(response => {
	          // 对响应的结果进行转换和处理
	          return response
	      },error => {
	          throw error
	      })
	  }
	  //3.创建适配器
	  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) return
	              if(xhr.status >= 200 && xhr.status < 300){
	                  resolve({
	                      config:config, // 配置对象
	                      data: xhr.response, // 响应体
	                      headers: xhr.getAllResponseHeaders(), // 响应头
	                      request: xhr, // xhr 请求对象
	                      status: xhr.status, // 响应状态码
	                      statusText: xhr.statusText,
	                  })
	              } else {
	                  reject()
	              }
	          }
	      })
	  }
	  // 4.创建axios 函数
	  let axios = Axios.prototype.request.bind(null)
	  axios({
	      method: 'GET',
	      url: 'http://localhost:3000/posts'
	  }).then(response => {
	      console.log(response)
	  })

模拟实现axios拦截器功能

	//1. 构造函数
     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 的前面,request.handles=[]
          * 响应拦截器,添加到 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)
         })
         // 遍历
         while(chains.length > 0) {
             // shift() 取出数据第一个,会改变原数据
             promise = promise.then(chains.shift(), chains.shift())
         }
         return promise
     }

     // 发送请求
     function dispatchRequest(config){
         return new Promise((resolve, reject) => {
             resolve({
                 status:200,
                 statusText: 'ok'
             })
         })
     }
    
     // 拦截器管理器构造函数
     function InterceptorManager() {
         this.handlers = []
     }
     InterceptorManager.prototype.use = function(fulfilled, rejected){
         this.handlers.push({fulfilled, rejected})
     }

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

     /**
      * 设置请求拦截器, config 配置对象
      * 请求输出结果:
      * 请求拦截器-2号
      * 请求拦截器-1号
      * 响应拦截器-1号
      * 响应拦截器-2号
      * Axios.prototype.request 里的 chains.unshift 处理的结果
      */
     axios.interceptors.request.use(function one(config){
         console.log('请求拦截器-1号')
     }, function(){})
     axios.interceptors.request.use(function tow(config){
         console.log('请求拦截器-2号')
     }, function(){})
     // 设置响应拦截器
     axios.interceptors.response.use(function one(response){
         console.log('响应拦截器-1号')
     },function(){})
     axios.interceptors.response.use(function tow(response){
         console.log('响应拦截器-2号')
     },function(){})

     axios({
         method: 'GET',
         url: 'http://localhost:3000/posts'
     }).then(response => {
         console.log(response)
     })

模拟axios取消请求功能

XMLHttpRequest 取消请求方法 - abort()

<body>
    <button>发送</button>
    <button>取消</button>
    <script>
        //1. 构造函数
        function Axios(config){
           this.config = config
        }
        // 发送请求
        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 ) return
                    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(resolve => {
                        xhr.abort()
                    })
                }
            })
        }

        // CancelToken 构造函数
        function CancelToken(executor){
            // 声明一个变量
            var resolvePromise;
            // 为实例对象添加属性
            this.promise = new Promise(resolve => {
                // 将resolve 赋值给 resolvePromise 所以当调用 resolvePromise()等于 resolve() (指针指引类型)
                resolvePromise = resolve
            })
            // 调用 executor 函数
            executor(function(){
                // 执行 resolvePromise 函数
                resolvePromise()
            })
        }

        // 创建axios 函数
        const context = new Axios({})
        const axios = Axios.prototype.request.bind(context)

        // 以上为模拟实现的代码
        const btns = document.querySelectorAll('button')
        let cancel = null
        btns[0].onclick = function() {
            // 检测上一次请求是否已经完成
            if(cancel !==null ){
                cancel()
            }
            // 创建cancelToken的值
            let cancelToken =new CancelToken(function(c){
                // 将c赋值给 cancel ,因为c是函数,所以当调用 cancel()等于 c() (指针指引类型)
                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>
</body>
总结

1、语法上 axios 不是 Axios 的实例:
因为是先造实例,再造一个函数,再把实例原型上的方法,往函数原型上放,再把实例对象的身上的属性加到函数对象身上,最终再把函数返回axios
2、功能上 axios 是 Axios 的实例:
因为 axios 拥有 Axios 实例对象的方法,通过扩展的方式,把 Axios 实例对象的方法绑定到 axios 身上
3、axios 是 Axios.prototype.request 函数 bind() 返回的函数
4、axios 作为对象有 Axios 原型对象上的所有方法,有 Axios 对象上所有属性

你可能感兴趣的:(javascript,原型模式,前端)