扩展方法
需求
为了用户更加方便地使用 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
类型声明
export interface Axios {request(config: AxiosRequestConfig): AxiosPromiseget(url: string, config?: AxiosRequestConfig): AxiosPromisehead(url: string, config?: AxiosRequestConfig): AxiosPromisedelete(url: string, config?: AxiosRequestConfig): AxiosPromiseoptions(url: string, config?: AxiosRequestConfig): AxiosPromisepost(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromiseput(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromisepatch(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise}
dispatchRequest
把之前axios.ts的代码复制到core/dispatchRequest.ts,因为之前的axios主要功能是发送请求
import { AxiosRequestConfig, AxiosResponse } from '../types/index'import xhr from './xhr'import { buildRUL } from '../helpers/url'import { transformRequest, transformResponse } from '../helpers/data'import { processHeaders } from '../helpers/headers'export default function dispatchRequest(config: AxiosRequestConfig) {return xhr(processConfig(config)).then(response => {return transformResponseData(response)})}// 处理请求参数function processConfig(config: AxiosRequestConfig): AxiosRequestConfig {config.url = transformUrl(config)// 在处理data之前处理headersconfig.headers = transformHeader(config)config.data = transformRequestData(config)return config}// 转换urlfunction transformUrl(config: AxiosRequestConfig) {const { url, params } = configreturn buildRUL(url || '', params)}// 转换request datafunction transformRequestData(config: AxiosRequestConfig) {return transformRequest(config.data)}// 转换headersfunction transformHeader(config: AxiosRequestConfig) {const { headers, data } = configreturn processHeaders(headers, data)}// 转换response datafunction transformResponseData(response: AxiosResponse) {response.data = transformResponse(response.data)return response}
声明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>
}
