温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

axios拦截器管理类链式调用怎么实现

发布时间:2022-08-27 11:35:04 来源:亿速云 阅读:228 作者:iii 栏目:开发技术

今天小编给大家分享一下axios拦截器管理类链式调用怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

    axios库的拦截器使用

    我们知道axios库的拦截器的使用方式如下:

    // 添加一个请求拦截器
    axios.interceptors.request.use(function (config) {
      // 在发送请求之前可以做一些事情
      return config;
    }, function (error) {
      // 处理请求错误
      return Promise.reject(error);
    });
    // 添加一个响应拦截器
    axios.interceptors.response.use(function (response) {
      // 处理响应数据
      return response;
    }, function (error) {
      // 处理响应错误
      return Promise.reject(error);
    });

    在 axios 对象上有一个 interceptors 对象属性,该属性又有 request 和 response 2 个属性,它们都有一个 use 方法,use 方法支持 2 个参数,第一个参数类似 Promise.then 的 resolve 函数,第二个参数类似 Promise.then 的 reject 函数。我们可以在 resolve 函数和 reject 函数中执行同步代码或者是异步代码逻辑。

    并且我们是可以添加多个拦截器的,拦截器的执行顺序是链式依次执行的方式。对于 request 拦截器,后添加的拦截器会在请求前的过程中先执行;对于 response 拦截器,先添加的拦截器会在响应后先执行。

    axios.interceptors.request.use(config => {
      config.headers.test += '1'
      return config
    })
    axios.interceptors.request.use(config => {
      config.headers.test += '2'
      return config
    })

    此外,我们也可以支持删除某个拦截器,如下:

    const myInterceptor = axios.interceptors.request.use(function () {/*...*/})
    axios.interceptors.request.eject(myInterceptor)

    整体设计

    我们先用一张图来展示一下拦截器工作流程:

    axios拦截器管理类链式调用怎么实现

    整个过程是一个链式调用的方式,并且每个拦截器都可以支持同步和异步处理,我们自然而然地就联想到使用 Promise 链的方式来实现整个调用过程。

    在这个 Promise 链的执行过程中,请求拦截器 resolve 函数处理的是 config 对象,而相应拦截器 resolve 函数处理的是 response 对象。

    在了解了拦截器工作流程后,我们先要创建一个拦截器管理类,允许我们去添加 删除和遍历拦截器。

    拦截器管理类实现

    根据需求,axios 拥有一个 interceptors 对象属性,该属性又有 request 和 response 2 个属性,它们对外提供一个 use 方法来添加拦截器,我们可以把这俩属性看做是一个拦截器管理对象。

    use 方法支持 2 个参数,第一个是 resolve 函数,第二个是 reject 函数,对于 resolve 函数的参数,请求拦截器是 AxiosRequestConfig 类型的,而响应拦截器是 AxiosResponse 类型的;而对于 reject 函数的参数类型则是 any 类型的。

    根据上述分析,我们先来定义一下拦截器管理对象对外的接口。

    接口定义

    这里我们定义了 AxiosInterceptorManager 泛型接口,因为对于 resolve 函数的参数,请求拦截器和响应拦截器是不同的。

    export interface AxiosInterceptorManager<T> {
      use(resolved: ResolvedFn<T>, rejected?: RejectedFn): number
      eject(id: number): void
    }
    export interface ResolvedFn<T=any> {
      (val: T): T | Promise<T>
    }
    export interface RejectedFn {
      (error: any): any
    }

    代码实现

    import { ResolvedFn, RejectedFn } from '../types'
    interface Interceptor<T> {
      resolved: ResolvedFn<T>
      rejected?: RejectedFn
    }
    export default class InterceptorManager<T> {
      private interceptors: Array<Interceptor<T> | null>
      constructor() {
        // 拦截器数组
        this.interceptors = []
      }
      // 收集拦截器  
      use(resolved: ResolvedFn<T>, rejected?: RejectedFn): number {
        this.interceptors.push({
          resolved,
          rejected
        })
        return this.interceptors.length - 1
      }
      // 遍历用户写的拦截器,并执行fn函数把拦截器作为参数传入
      forEach(fn: (interceptor: Interceptor<T>) => void): void {
        this.interceptors.forEach(interceptor => {
          if (interceptor !== null) {
            fn(interceptor)
          }
        })
      }
      eject(id: number): void {
        if (this.interceptors[id]) {
          // 置为null,不能直接删除
          this.interceptors[id] = null
        }
      }
    }

    我们定义了一个 InterceptorManager 泛型类,内部维护了一个私有属性 interceptors,它是一个数组,用来存储拦截器。该类还对外提供了 3 个方法,其中 use 接口就是添加拦截器到 interceptors 中,并返回一个 id 用于删除;

    forEach 接口就是遍历 interceptors 用的,它支持传入一个函数,遍历过程中会调用该函数,并把每一个 interceptor 作为该函数的参数传入;eject 就是删除一个拦截器,通过传入拦截器的 id 删除。

    链式调用实现

    当我们实现好拦截器管理类,接下来就是在 Axios 中定义一个 interceptors 属性,它的类型如下:

    interface Interceptors {
      request: InterceptorManager<AxiosRequestConfig>
      response: InterceptorManager<AxiosResponse>
    }
    export default class Axios {
      interceptors: Interceptors
      constructor() {
        this.interceptors = {
          request: new InterceptorManager<AxiosRequestConfig>(),
          response: new InterceptorManager<AxiosResponse>()
        }
      }
    }

    Interceptors 类型拥有 2 个属性,一个请求拦截器管理类实例,一个是响应拦截器管理类实例。我们在实例化 Axios 类的时候,在它的构造器去初始化这个 interceptors 实例属性。

    接下来,我们修改 request 方法的逻辑,添加拦截器链式调用的逻辑:

    interface PromiseChain {
      resolved: ResolvedFn | ((config: AxiosRequestConfig) => AxiosPromise)
      rejected?: RejectedFn
    }
    request(url: any, config?: any): AxiosPromise {
      if (typeof url === 'string') {
        if (!config) {
          config = {}
        }
        config.url = url
      } else {
        config = url
      }
      // 定义一个数组,这个数组就是要执行的任务链,默认有一个真正发送请求的任务
      const chain: PromiseChain[] = [{
        resolved: dispatchRequest,
        rejected: undefined
      }]
      // 把用户定义的请求拦截器存放到任务链中,请求拦截器最后注册的最先执行,所以使用unshift方法
      this.interceptors.request.forEach(interceptor => {
        chain.unshift(interceptor)
      })
      // 把响应拦截器存放到任务链中
      this.interceptors.response.forEach(interceptor => {
        chain.push(interceptor)
      })
      // 利用config初始化一个promise
      let promise = Promise.resolve(config)
      // 遍历任务链
      while (chain.length) {
        // 取出任务链的首个任务
        const { resolved, rejected } = chain.shift()!
        // resolved的执行时机是就是上一个promise执行resolve()的时候,这样就形成了链式调用
        promise = promise.then(resolved, rejected)
      }
      return promise
    }

    首先,构造一个 PromiseChain 类型的数组 chain,并把 dispatchRequest 函数赋值给 resolved 属性;接着先遍历请求拦截器插入到 chain 的前面;然后再遍历响应拦截器插入到 chain 后面。

    接下来定义一个已经 resolve 的 promise,循环这个 chain,拿到每个拦截器对象,把它们的 resolved 函数和 rejected 函数添加到 promise.then 的参数中,这样就相当于通过 Promise 的链式调用方式,实现了拦截器一层层的链式调用的效果。

    注意我们拦截器的执行顺序,对于请求拦截器,先执行后添加的,再执行先添加的;而对于响应拦截器,先执行先添加的,后执行后添加的。

    以上就是“axios拦截器管理类链式调用怎么实现”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注亿速云行业资讯频道。

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI