扩展方法

需求

为了用户更加方便地使用 axios 发送请求,我们可以为所有支持请求方法扩展一些接口:

  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.head(url[, config])
  • axios.options(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])

如果使用了这些方法,我们就不必在 config 中指定 url、method、data 这些属性了。
从需求上来看,axios 不再单单是一个方法,更像是一个混合对象,本身是一个方法,又有很多方法属性,接下来我们就来实现这个混合对象

创建Axios

通过Axios类,管理所有的请求方法

类型声明

  1. export interface Axios {
  2. request(config: AxiosRequestConfig): AxiosPromise
  3. get(url: string, config?: AxiosRequestConfig): AxiosPromise
  4. head(url: string, config?: AxiosRequestConfig): AxiosPromise
  5. delete(url: string, config?: AxiosRequestConfig): AxiosPromise
  6. options(url: string, config?: AxiosRequestConfig): AxiosPromise
  7. post(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise
  8. put(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise
  9. patch(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise
  10. }

dispatchRequest

把之前axios.ts的代码复制到core/dispatchRequest.ts,因为之前的axios主要功能是发送请求

  1. import { AxiosRequestConfig, AxiosResponse } from '../types/index'
  2. import xhr from './xhr'
  3. import { buildRUL } from '../helpers/url'
  4. import { transformRequest, transformResponse } from '../helpers/data'
  5. import { processHeaders } from '../helpers/headers'
  6. export default function dispatchRequest(config: AxiosRequestConfig) {
  7. return xhr(processConfig(config)).then(response => {
  8. return transformResponseData(response)
  9. })
  10. }
  11. // 处理请求参数
  12. function processConfig(config: AxiosRequestConfig): AxiosRequestConfig {
  13. config.url = transformUrl(config)
  14. // 在处理data之前处理headers
  15. config.headers = transformHeader(config)
  16. config.data = transformRequestData(config)
  17. return config
  18. }
  19. // 转换url
  20. function transformUrl(config: AxiosRequestConfig) {
  21. const { url, params } = config
  22. return buildRUL(url || '', params)
  23. }
  24. // 转换request data
  25. function transformRequestData(config: AxiosRequestConfig) {
  26. return transformRequest(config.data)
  27. }
  28. // 转换headers
  29. function transformHeader(config: AxiosRequestConfig) {
  30. const { headers, data } = config
  31. return processHeaders(headers, data)
  32. }
  33. // 转换response data
  34. function transformResponseData(response: AxiosResponse) {
  35. response.data = transformResponse(response.data)
  36. return response
  37. }

声明class Axios


import { AxiosRequestConfig, Method } from '../types/index'
import dispatchRequest from './dispacthRequest'

export default class Axios {
  request(config: AxiosRequestConfig) {
    return dispatchRequest(config)
  }

  // _requestWithNoData
  get(url: string, config?: AxiosRequestConfig) {
    return this._requestWithNoData('get', url, config)
  }
  head(url: string, config?: AxiosRequestConfig) {
    return this._requestWithNoData('head', url, config)
  }
  delete(url: string, config?: AxiosRequestConfig) {
    return this._requestWithNoData('delete', url, config)
  }
  options(url: string, config?: AxiosRequestConfig) {
    return this._requestWithNoData('options', url, config)
  }

  // _requestWithData
  post(url: string, data?: any, config?: AxiosRequestConfig) {
    return this._requestWithData('post', url, data, config)
  }
  put(url: string, data?: any, config?: AxiosRequestConfig) {
    return this._requestWithData('put', url, data, config)
  }
  patch(url: string, data?: any, config?: AxiosRequestConfig) {
    return this._requestWithData('patch', url, data, config)
  }

  _requestWithNoData(method: Method, url: string, config?: AxiosRequestConfig) {
    return this.request(
      Object.assign(config || {}, {
        method,
        url
      })
    )
  }

  _requestWithData(method: Method, url: string, data?: any, config?: AxiosRequestConfig) {
    return this.request(
      Object.assign(config || {}, {
        method,
        url,
        data
      })
    )
  }
}

extend 混合对象合并

export function extend<T, U>(to: T, from: U): T & U {
  for (let key in from) {
    (to as T & U)[key] = from[key] as any
  }
  return to as T & U
}

创建Axios实例

不再简单导出一个 axios方法,而是通过创建实例,合并混合对象,实现需求。

import { AxiosInstance } from './types'
import Axios from './core/Axios'
import { extend } from './helpers/util'

function createInstance(): AxiosInstance {
  const context = new Axios()
  // request function
  const instance = Axios.prototype.request.bind(context)

  // 把实例axios所有方法挂载到instance
  extend(instance, context)

  return instance as AxiosInstance
}

const axios = createInstance()

export default axios

实现接口重载

需求

axios支持传入两个参数,如 :

axios({
  url: '/extend/post',
  method: 'post',
  data: { name: '接口重载' }
}).then(res => {
  console.log(res)
})
axios('/extend/post', {
  method: 'post',
  data: { name: '接口重载' }
}).then(res => {
  console.log(res)
})

修改类型声明

export interface AxiosInstance extends Axios {
  (config: AxiosRequestConfig): AxiosPromise
  // overload
  (url: string, config?: AxiosRequestConfig): AxiosPromise
}

request函数参数处理

export default class Axios {
  request(url: string | AxiosRequestConfig, config?: AxiosRequestConfig) {
    if (typeof url === 'string') {
      if (!config) {
        config = {}
      }
      config.url = url
    } else {
      config = url
    }

    return dispatchRequest(config)
  }
  // ...
}

数据类型添加泛型

添加泛型声明,不需修改逻辑。


export interface AxiosResponse<T = any> {
  headers: any
  status: number
  data: T
  config: AxiosRequestConfig
  request: any
}

export interface AxiosPromise<T = any> extends Promise<AxiosResponse<T>> { }

export interface AxiosError extends Error {
  isAxiosError: boolean
  config: AxiosRequestConfig
  code?: string | null
  request?: any
  response?: AxiosResponse
}

export interface Axios {
  request<T = any>(config: AxiosRequestConfig): AxiosPromise<T>

  get<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>
  head<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>
  delete<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>
  options<T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>

  post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise<T>
  put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise<T>
  patch<T = any>(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise<T>
}

export interface AxiosInstance extends Axios {
  <T = any>(config: AxiosRequestConfig): AxiosPromise<T>
  // overload
  <T = any>(url: string, config?: AxiosRequestConfig): AxiosPromise<T>
}