美文网首页
从重构Axios学到了什么

从重构Axios学到了什么

作者: zdxhxh | 来源:发表于2020-02-16 23:33 被阅读0次

    导语

    最近,看了黄奕大神的typescript重构axios,并自己亲自实践了一遍。实践是检验真理的唯一标准。

    记录一下axios中用到的骚操作

    一、使用Promise数组实现拦截器

    axios中可以实现请求还有响应的拦截器。如下 :

    // use方法接收两个函数,并使用一个对象包裹存入自身
    // use(
    //   resolved,
    //   rejected
    // )
    instance.interceptors.request.use(
      function(config: any) {
        // 发送请求前做什么事
        return config;
      },
      function(error: any) {
        // 请求错误时做些事
        return Promise.reject(error);
      }
    );
    
    instance.interceptors.response.use(
      (response: any) => {
        return response;
      },
      (error: any) => {
        return Promise.reject(error);
      }
    );
    

    axios内部原理:

    const chain = [
      {
        resolved: dispatchRequest,  // 这是一个发送XMLHttpRequest的方法,并在onreadystatechange下resolve response的
        rejected: undefined
      }
    ]
    const promise = Promise.resolve(config)
    interceptors.request.forEach(interceptor=>{
      chain.unshift(interceptor)
    })
    interceptors.response.forEach(interceptor=>{
      chain.push(interceptor)
    })
    // 上面这个操作可以将chain 变为以下数据结构
    // [request拦截器2,requset拦截器1,xhr请求,response拦截器1,response拦截器2]
    let promise = Promise.resolve(config)
    while (chain.length) {
      const { resolved, rejected } = chain.shift()
      promise = promise.then(resolved, rejected)
    }
    

    总结 : 就是数组结合promise.then不断将数据传递。

    二、深合并deepMerge

    deepMerge接收多个对象,然后返回一个结合这多个对象的船新版本对象

    export function isPlainObject(val: any): val is Object {
      return toString.call(val) === '[object Object]'
    }
    export function deepMerge(...objs: any[]) {
      const result = Object.create(null)  // 使用Object.create(null) 创造的对象没有原型,所以比较节省内存
      // 遍历每一个入参对象
      objs.forEach(obj => {
        if (obj) {
          Object.keys(obj).forEach(key => {
            const val = obj[key]
            // 判断该属性是不是一个对象
            if (isPlainObject(val)) {
              // 判断result该属性是否已存在
              if (isPlainObject(result[key])) {
                // 将其继续合并
                result[key] = deepMerge(result[key], val)
              } else {
                result[key] = deepMerge({}, val)
              }
            } else {
              result[key] = val
            }
          })
        }
      })
      return result
    }
    

    总结: 深合并不是真合并,而是对传入的每个对象进行深拷贝,递归太难想了 - - !

    三、如何判断一个URL是否与主域名一致

    // 同域名判断
    // 通过传入的url,将其设置为一个a标签的url,然后获取该DOM的protocol(协议)、host(域名)属性
    // 传入url可以通过 axios({url:''})拿到
    // 当前页面的url可以通过window.location.href拿到
    
    export function isURLSameOrigin(requestURL: string): boolean {
      const parsedOrigin = resolveURL(requestURL)
      return (
        parsedOrigin.protocol === currentOrigin.protocol && parsedOrigin.host === currentOrigin.host
      )
    }
    
    const urlParsingNode = document.createElement('a')
    const currentOrigin = resolveURL(window.location.href)
    
    function resolveURL(url: string): URLOrigin {
      urlParsingNode.setAttribute('href', url)
      const { protocol, host } = urlParsingNode
      return {
        protocol,
        host
      }
    }
    

    四、使用Promise当一个状态机

    该方法是在axios 的CancelToken使用。

    let cancel 
    
    // 实际上这种xhr是异步发送的 也就是包裹了promise发送的
    axios({
      method: 'get',
      url: '/cancel/get',
      data: {
        a: 1,
        b: 2
      },
      CancelToken : new CancelToken(c=> {cancel = c })
    })
    // 所以这里必须使用setTimeout
    setTimeout(() => {
      console.log(cancel('取消了异步请求'))
    });
    
    let c
    const promise = new Promise(resovle=>{
      c = resolve
    })
    promise.then(()=> {
      console.log('存入一个回调了')
    })
    promise.then(()=> {
      console.log('存入一个回调了')
    })
    promise.then(()=> {
      console.log('存入一个回调了')
    })
    c();
    

    原理 : 将Promise的excutor函数的入参使用闭包或者其他手段缓存,当使用then往该promise实例的onResolveCallbacks存入回调函数后,再调用resolve()将其触发。

    相关文章

      网友评论

          本文标题:从重构Axios学到了什么

          本文链接:https://www.haomeiwen.com/subject/xdjvfhtx.html