设置默认配置
// ./src/default.ts
import { AxiosRequestConfig } from './types'
const defaults: AxiosRequestConfig = {
method: 'get',
timeout: 0,
headers: {
common: {
Accept: 'application/json, text/plain, */*'
}
}
}
const methodsNoData = ['delete', 'get', 'head', 'options']
methodsNoData.forEach(method => {
defaults.headers[method] = {}
})
const methodsWithData = ['post', 'put', 'patch']
methodsWithData.forEach(method => {
defaults.headers[method] = {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
export default defaults
在Axios类中使用默认配置
// ./src/core/Axios.ts
//...
export default class Axios {
defaults: AxiosRequestConfig
interceptors: Interceptors
constructor(initconfig: AxiosRequestConfig) {
this.defaults = initconfig
this.interceptors = {
request: new InterceptorManager<AxiosRequestConfig>(),
response: new InterceptorManager<AxiosResponse>()
}
}
//...
}
//...
修改接口
// ./src/type/index.ts
//...
export interface Axios {
defaults: AxiosRequestConfig
//...
}
在实例化过程中使用
// ./src/index.ts
import { AxiosInstance, AxiosRequestConfig } from './types'
import Axios from './core/Axios'
import { extend } from './helpers/util'
import defaults from './default'
function createInstance(config: AxiosRequestConfig): AxiosInstance {
const context = new Axios(config)
const instance = Axios.prototype.request.bind(context)
extend(instance, context)
return instance as AxiosInstance
}
const axios = createInstance(defaults)
export default axios
合并策略
// ./src/core/mergeConfig.ts
import { AxiosRequestConfig } from "../types";
import { isPlainObject, deepMerge } from "../helpers/util";
const strats = Object.create(null)
function defaultStrat (val1: any, val2: any): any {
return typeof val2 !== 'undefined' ? val2 : val1
}
function fromVal2Strat (val1: any, val2: any): any {
if (typeof val2 !== 'undefined') {
return val2
}
}
function deepMergeStrat(val1: any, val2: any): any {
if (isPlainObject(val2)) {
return deepMerge(val1, val2)
} else if (typeof val2 !== 'undefined') {
return val2
} else if (isPlainObject(val1)) {
return deepMerge(val1)
} else if (typeof val1 !== 'undefined') {
return val1
}
}
const stratKeysFromVal2 = ['url', 'params', 'data']
stratKeysFromVal2.forEach(key => {
strats[key] = fromVal2Strat
})
const stratKeysDeepMerge = ['headers']
stratKeysDeepMerge.forEach(key => {
strats[key] = deepMergeStrat
})
export default function mergeConfig(config1: AxiosRequestConfig, config2?: AxiosRequestConfig) {
if (!config2) {
config2 = {}
}
const config = Object.create(null)
for (const key in config2) {
mergeField(key)
}
for (const key in config1) {
if (!config2[key]) {
mergeField(key)
}
}
function mergeField (key: string): void {
const strat = strats[key] || defaultStrat
config[key] = strat(config1[key], config2![key])
}
return config
}
对象深拷贝
// ./src/helpers/util.ts
export function deepMerge (...objs: any[]): any {
const result = Object.create(null)
objs.forEach(obj => {
if (obj) {
Object.keys(obj).forEach(key => {
const val = obj[key]
if (isPlainObject(val)) {
if (isPlainObject(result[key])) {
result[key] = deepMerge(result[key], val)
} else {
result[key] = deepMerge({}, val)
}
} else {
result[key] = val
}
})
}
})
return result
}
合并配置
// ./src/core/Axios.ts
//...
export default class Axios {
//...
request(url: any, config?: any): AxiosPromise {
if (typeof url === 'string') {
if (!config) {
config = {}
}
config.url = url
} else {
config = url
}
config = mergeConfig(this.defaults, config)
const chain: PromiseChain<any>[] = [
{
resolved: dispatchRequest,
rejected: undefined
}
]
//...
return promise
}
//...
}
提取headers内部属性
// ./src/helpers/headers.ts
import { isPlainObject, deepMerge } from './util'
import { Method } from '../types'
export function flattenHeaders (headers: any, method: Method): any {
if (!headers) {
return headers
}
headers = deepMerge(headers.common, headers[method], headers)
const methodsToDelete = ['delete', 'get', 'head', 'options', 'post', 'put', 'patch', 'common']
methodsToDelete.forEach(method => {
delete headers[method]
})
return headers
}
// ./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, flattenHeaders } from '../helpers/headers'
//...
function processConfig(config: AxiosRequestConfig): void {
config.url = transformURL(config)
config.headers = transformHeaders(config)
config.data = transformRequestData(config)
config.headers = flattenHeaders(config.headers, config.method!)
}
//...
测试
安装依赖
npm i qs
npm i @types/qs
测试页面
./examples/config/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Config</title>
</head>
<body>
<script src="/__build__/config.js"></script>
</body>
</html>
测试代码
// ./exapmles/config/app.ts
import axios from '../../src/index'
import qs from 'qs'
axios.defaults.headers.common['test2'] = '123'
axios({
url: '/config/post',
method: 'post',
data: qs.stringify({
a: 1
}),
headers: {
test: '321'
}
}).then(res => {
console.log(res.data)
})
测试路由
router.post('/config/post', (req, res) => {
res.json(req.body)
})
请求和响应配置化
修改默认配置
修改接口
// ./src/type/index.ts
export interface AxiosRequestConfig {
url?: string
method?: Method
data?: any
params?: any
headers?: any
responseType?: XMLHttpRequestResponseType
timeout?: number
transformRequest?: AxiosTransformer | AxiosTransformer[]
transformResponse?: AxiosTransformer | AxiosTransformer[]
[propName: string]: any
}
export interface AxiosTransformer {
(data: any, headers?: any): any
}
修改默认配置
// ./src/default.ts
import { AxiosRequestConfig } from './types'
import { processHeaders } from './helpers/headers'
import { transformRequest, transformResponse } from './helpers/data'
const defaults: AxiosRequestConfig = {
method: 'get',
timeout: 0,
headers: {
common: {
Accept: 'application/json, text/plain, */*'
}
},
transformRequest: [
function(data: any, headers: any): any {
processHeaders(headers, data)
return transformRequest(data)
}
],
transformResponse: [
function(data: any): any {
return transformResponse(data)
}
]
}
//...
export default defaults
transform逻辑重构
// ./src/core/transform.ts
import { AxiosTransformer } from "../types";
export default function (data: any, headers: any, fns?: AxiosTransformer | AxiosTransformer[]): any {
if (!fns) {
return data
}
if (!Array.isArray(fns)) {
fns = [fns]
}
fns.forEach(fn => {
data = fn(data, headers)
})
return data
}
在请求和响应中使用
// ./src/core/dispatchRequest.ts
import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from '../types'
import xhr from './xhr'
import { buildURL } from '../helpers/url'
import { flattenHeaders } from '../helpers/headers'
import transform from './transform'
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.data = transform(config.data, config.headers, config.transformRequest)
config.headers = flattenHeaders(config.headers, config.method!)
}
function transformURL(config: AxiosRequestConfig): string {
const { url, params } = config
return buildURL(url!, params)
}
function transformResponseData(res: AxiosResponse): AxiosResponse {
res.data = transform(res.data, res.headers, res.config.transformResponse)
return res
}
测试
// ./exapmles/config/app.ts
import axios, { AxiosTransformer } from '../../src/index'
import qs from 'qs'
axios({
transformRequest: [(function(data) {
return qs.stringify(data)
}), ...(axios.defaults.transformRequest as AxiosTransformer[])],
transformResponse: [...(axios.defaults.transformResponse as AxiosTransformer[]),
function(data) {
if (typeof data === 'object') {
data.b = 2
}
return data
}],
url: '/config/post',
method: 'post',
data: {
a: 1
}
}).then(res => {
console.log(res.data)
})
扩展axios.create静态接口
接口定义
// ./src/type/index.ts
export interface AxiosStatic extends AxiosInstance {
create(config?: AxiosRequestConfig): AxiosInstance
}
修改实例创建方法
// ./src/index.ts
import { AxiosRequestConfig, AxiosStatic } from './types'
import Axios from './core/Axios'
import { extend } from './helpers/util'
import defaults from './default'
import mergeConfig from './core/mergeConfig'
function createInstance(config: AxiosRequestConfig): AxiosStatic {
const context = new Axios(config)
const instance = Axios.prototype.request.bind(context)
extend(instance, context)
return instance as AxiosStatic
}
const axios = createInstance(defaults)
axios.create = function create(config) {
return createInstance(mergeConfig(defaults, config))
}
export default axios
测试
// ./exapmles/config/app.ts
import axios, { AxiosTransformer } from '../../src/index'
import qs from 'qs'
const instance = axios.create({
transformRequest: [(function(data) {
return qs.stringify(data)
}), ...(axios.defaults.transformRequest as AxiosTransformer[])],
transformResponse: [...(axios.defaults.transformResponse as AxiosTransformer[]),
function(data) {
if (typeof data === 'object') {
data.b = 2
}
return data
}]
})
instance({
url: '/config/post',
method: 'post',
data: {
a: 1
}
}).then(res => {
console.log(res.data)
})