// ./src/type/index.ts
export type Method =
| 'get'
| 'GET'
| 'delete'
| 'DELETE'
| 'head'
| 'HEAD'
| 'options'
| 'OPTIONS'
| 'post'
| 'POST'
| 'put'
| 'PUT'
| 'patch'
| 'PATCH'
export interface AxiosRequestConfig {
url?: string
method?: Method
data?: any
params?: any
headers?: any
responseType?: XMLHttpRequestResponseType
timeout?: number
}
export interface AxiosResponse {
data: any
status: number
statusText: string
headers: any
config: AxiosRequestConfig
request: any
}
export interface AxiosPromise extends Promise {}
export interface AxiosError extends Error {
isAxiosError: boolean
config: AxiosRequestConfig
code?: string | null
request?: any
response?: AxiosResponse
}
export interface Axios {
request(config: AxiosRequestConfig): AxiosPromise
get(url: string, config?: AxiosRequestConfig): AxiosPromise
delete(url: string, config?: AxiosRequestConfig): AxiosPromise
head(url: string, config?: AxiosRequestConfig): AxiosPromise
options(url: string, config?: AxiosRequestConfig): AxiosPromise
post(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise
put(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise
patch(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise
}
export interface AxiosInstance extends Axios {
(config: AxiosRequestConfig): AxiosPromise
}
// ./src/core/xhr.ts
import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from '../types'
import { parseHeaders } from '../helpers/headers'
import { createError } from '../helpers/error'
export default function xhr(config: AxiosRequestConfig): AxiosPromise {
return new Promise((resolve, reject) => {
const { data = null, url, method = 'get', headers, responseType, timeout } = config
const request = new XMLHttpRequest()
if (responseType) {
request.responseType = responseType
}
if (timeout) {
request.timeout = timeout
}
request.open(method.toUpperCase(), url!, true)
request.onreadystatechange = function handleLoad() {
if (request.readyState !== 4) {
return
}
if (request.status === 0) {
return
}
const responseHeaders = parseHeaders(request.getAllResponseHeaders())
const responseData = responseType !== 'text' ? request.response : request.responseText
const response: AxiosResponse = {
data: responseData,
status: request.status,
statusText: request.statusText,
headers: responseHeaders,
config,
request
}
handleResponse(response)
}
request.onerror = function handleError() {
reject(createError('Network Error', config, null, request))
}
request.ontimeout = function handleTimeout() {
reject(createError(`Timeout of ${timeout} ms exceeded`, config, 'ECONNABORTED', request))
}
Object.keys(headers).forEach(name => {
if (data === null && name.toLowerCase() === 'content-type') {
delete headers[name]
} else {
request.setRequestHeader(name, headers[name])
}
})
request.send(data)
function handleResponse(response: AxiosResponse): void {
if (response.status >= 200 && response.status < 300) {
resolve(response)
} else {
reject(
createError(
`Request failed with status code ${response.status}`,
config,
null,
request,
response
)
)
}
}
})
}
// ./src/core/dispatchRequest.ts
import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from '../types'
import xhr from './xhr'
import { buildURL } from '../helpers/url'
import { transformRequest, transformResponse } from '../helpers/data'
import { processHeaders } from '../helpers/headers'
export default function dispatchRequest(config: AxiosRequestConfig): AxiosPromise {
// TODO
processConfig(config)
return xhr(config).then(res => {
return transformResponseData(res)
})
}
function processConfig(config: AxiosRequestConfig): void {
config.url = transformURL(config)
config.headers = transformHeaders(config)
config.data = transformRequestData(config)
}
function transformURL(config: AxiosRequestConfig): string {
const { url, params } = config
return buildURL(url!, params)
}
function transformRequestData(config: AxiosRequestConfig): any {
return transformRequest(config.data)
}
function transformHeaders(config: AxiosRequestConfig): any {
const { headers = {}, data } = config
return processHeaders(headers, data)
}
function transformResponseData(res: AxiosResponse): AxiosResponse {
res.data = transformResponse(res.data)
return res
}
// ./src/core/Axios.ts
import { AxiosRequestConfig, AxiosPromise, Method } from "../types";
import dispatchRequest from './dispatchRequest'
export default class Axios {
request(config: AxiosRequestConfig): AxiosPromise {
return dispatchRequest(config)
}
get(url: string, config?: AxiosRequestConfig): AxiosPromise {
return this._requestMethodWithoutData('get', url, config)
}
delete(url: string, config?: AxiosRequestConfig): AxiosPromise {
return this._requestMethodWithoutData('delete', url, config)
}
head(url: string, config?: AxiosRequestConfig): AxiosPromise {
return this._requestMethodWithoutData('head', url, config)
}
options(url: string, config?: AxiosRequestConfig): AxiosPromise {
return this._requestMethodWithoutData('options', url, config)
}
post(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise {
return this._requestMethodWithData('post', url, data, config)
}
put(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise {
return this._requestMethodWithData('put', url, data, config)
}
patch(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise {
return this._requestMethodWithData('patch', url, data, config)
}
_requestMethodWithoutData(method: Method, url: string, config?: AxiosRequestConfig) {
return this.request(Object.assign(config || {}, {
method,
url
}))
}
_requestMethodWithData(method: Method, url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise {
return this.request(Object.assign(config || {}, {
method,
url,
data
}))
}
}
// ./src/helpers/util.ts
//...
export function extend(to: T, from: U): T & U {
for (const key in from) {
;(to as T & U)[key] = from[key] as any
}
return to as T&U
}
// ./src/index.ts
import { AxiosInstance } from "./types";
import Axios from "./core/Axios";
import { extend } from "./helpers/util";
function createInstance (): AxiosInstance {
const context = new Axios()
const instance = Axios.prototype.request.bind(context)
extend(instance, context)
return instance as AxiosInstance
}
const axios = createInstance()
export default axios
// ./examples/extend/index.html
Extend
// ./examples/extend/app.ts
import axios from '../../src/index'
axios({
url: '/extend/post',
method: 'post',
data: {
msg: 'hi'
}
})
axios.request({
url: '/extend/post',
method: 'post',
data: {
msg: 'hello'
}
})
axios.get('/extend/get')
axios.options('/extend/options')
axios.delete('/extend/delete')
axios.head('/extend/head')
axios.post('/extend/post', { msg: 'post' })
axios.put('/extend/put', { msg: 'put' })
axios.patch('/extend/patch', { msg: 'patch' })
// ./examples/server.js
router.get('/extend/get', (req, res) => {
res.json({
msg: 'hello world'
})
})
router.options('/extend/options', (req, res) => {
res.end()
})
router.delete('/extend/delete', (req, res) => {
res.end()
})
router.head('/extend/head', (req, res) => {
res.end()
})
router.post('/extend/post', (req, res) => {
res.json(req.body)
})
router.put('/extend/put', (req, res) => {
res.json(req.body)
})
router.patch('/extend/patch', (req, res) => {
res.json(req.body)
})
修改类型定义
./src/type/index.ts
export interface AxiosInstance extends Axios {
(config: AxiosRequestConfig): AxiosPromise
(url: string, config?: AxiosRequestConfig): AxiosPromise
}
// ./src/core/Axios.ts
import { AxiosRequestConfig, AxiosPromise, Method } from '../types'
import dispatchRequest from './dispatchRequest'
export default class Axios {
request(url: any, config?: any): AxiosPromise {
if (typeof url === 'string') {
if (!config) {
config = {}
}
config.url = url
} else {
config = url
}
return dispatchRequest(config)
}
//...
}
测试
axios({
url: '/extend/post',
method: 'post',
data: {
msg: 'hi'
}
})
axios('/extend/post', {
method: 'post',
data: {
msg: 'hello'
}
})
// ./src/type/index.ts
export type Method =
| 'get'
| 'GET'
| 'delete'
| 'DELETE'
| 'head'
| 'HEAD'
| 'options'
| 'OPTIONS'
| 'post'
| 'POST'
| 'put'
| 'PUT'
| 'patch'
| 'PATCH'
export interface AxiosRequestConfig {
url?: string
method?: Method
data?: any
params?: any
headers?: any
responseType?: XMLHttpRequestResponseType
timeout?: number
}
export interface AxiosResponse {
data: T
status: number
statusText: string
headers: any
config: AxiosRequestConfig
request: any
}
export interface AxiosPromise extends Promise> {}
export interface AxiosError extends Error {
isAxiosError: boolean
config: AxiosRequestConfig
code?: string | null
request?: any
response?: AxiosResponse
}
export interface Axios {
request(config: AxiosRequestConfig): AxiosPromise
get(url: string, config?: AxiosRequestConfig): AxiosPromise
delete(url: string, config?: AxiosRequestConfig): AxiosPromise
head(url: string, config?: AxiosRequestConfig): AxiosPromise
options(url: string, config?: AxiosRequestConfig): AxiosPromise
post(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise
put(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise
patch(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise
}
export interface AxiosInstance extends Axios {
(config: AxiosRequestConfig): AxiosPromise
(url: string, config?: AxiosRequestConfig): AxiosPromise
}
测试
// ./examples/extend/app.ts
import axios from '../../src/index'
interface ResponseData {
code: number
result: T
message: string
}
interface User {
name: string
age: number
}
function getUser (){
return axios>('/extend/user')
.then(res => res.data)
.catch(err => console.log(err))
}
async function test() {
const user = await getUser()
if (user) {
console.log(user.result.name)
}
}
test()
// ./examples/server.js
//...
router.get('/extend/user', (req, res) => {
res.json({
code: 0,
message: 'ok',
result: {
name: 'jack',
age: 18
}
})
})
//...