五、重构项目结构
2020-04-24 本文已影响0人
雪燃归来
在前面的几章内容中,我们已经实现了ts-axios的发送请求、处理请求、Promise化等操作,同时也处理错误的处理情况。本片我们还也需要在原先的基础上对项目的代码进行重新排版,对项目的目录结构进行相应的调整。仓库地址:https://github.com/antiai-Antiai/ts-axios
一、改动项目目录的原因
对于项目结构的变动,主要基于下面几点原因:
1、方便使用这使用,对外暴露出的接口要足够简单。
2、在项目的入口文件中除了要到处axios函数,还要导出我们内部定义的一些类型。
3、将核心的代码封装到core目录下,防止不小心篡改。
二、改动后项目的目录结构
![](https://img.haomeiwen.com/i10349654/bb72f18431a8c9d7.png)
我们把跟发送请求相关的处理都放在了core目录下。每个文件具体的作用如下表
文件位置 | 作用 |
---|---|
/axios.ts | axios入口文件,在这个文件中合并使用axios的的两种情况 |
/index.ts | 程序的入口文件,用于导出axios和内部定义的类型 |
/core/Axios.ts | Axios实例方法的定义文件 |
/core/dispatch.ts | Axios函数式使用的定义文件 |
/core/xhr.ts | xmlHttpRequest实例方法的实现 |
/helpers/data.ts | 处理请求过程中需要在body传入数据的情况 |
/helpers/error.ts | 处理请求中发生错误的情况 |
/helpers/header.ts | 处理请求头的操作 |
/helpers/url.ts | 处理get等请求时,重组url |
/helpers/utils.ts | 一些帮助类的函数 |
/types/index.ts | 类型定义文件 |
这些文件中,/helpers和/types目录下的文件,我们都比较熟悉,本次调整目录结构的时候也不曾有变化,所以下面我们来重点看一下/core目录下的文件和两个入口文件。
三、代码详解
下面,就将截止到当前的代码梳理一下。
(一)、核心代码(/core)
1、/core/dispatch.ts
import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from '../types'
import { buildURL } from '../helpers/url'
import { transformRequest, transformReponse } from '../helpers/data'
import { processHeaders } from '../helpers/header'
import xhr from './xhr'
function dispatch(config: AxiosRequestConfig): AxiosPromise {
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 = transformDataRequest(config)
}
function transformURL(config: AxiosRequestConfig): string {
const { url, params } = config
return buildURL(url!, params)
}
function transformDataRequest(config: AxiosRequestConfig): string {
return transformRequest(config.data)
}
function transformHeaders(config: AxiosRequestConfig): any {
const { headers = {}, data } = config
return processHeaders(headers, data)
}
function transformResponseData(res: AxiosResponse): AxiosResponse {
res.data = transformReponse(res.data)
return res
}
export default dispatch
这个文件中的代码主要是实现axios()函数式的调用,比如如下的调用方式。
axios({
method: 'get',
url: '/simple/get',
params: {
a: 1,
b: 2
}
})
2、/core/xhr.ts
import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from '../types'
import { parseHeaders } from '../helpers/header'
import { createError } from '../helpers/error'
export default function xhr(config: AxiosRequestConfig): AxiosPromise {
return new Promise((resolve, reject) => {
const { data, url, method = 'get', headers, responseType, timeout } = config
// 创建XHR请求对象的实例
const request = new XMLHttpRequest()
/**如果存在responseType */
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() {
reject(createError('Network Error!', config, null, request))
}
/**处理请求错误 */
request.ontimeout = function() {
reject(createError(`Timeout of ${timeout} ms exceeded`, config, null, 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
)
)
}
}
})
}
3、/core/Axios.ts
import { AxiosPromise, AxiosRequestConfig, Method } from '../types'
import dispatchRequest from './dispatch'
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, config?: AxiosRequestConfig, data?: any): AxiosPromise {
return this._requestMethodWithData('post', url, config, data)
}
put(url: string, config?: AxiosRequestConfig, data?: any): AxiosPromise {
return this._requestMethodWithData('put', url, config, data)
}
patch(url: string, config?: AxiosRequestConfig, data?: any): AxiosPromise {
return this._requestMethodWithData('patch', url, config, data)
}
_requestMethodWithoutData(method: Method, url: string, config?: AxiosRequestConfig) {
return this.request(Object.assign(config || {}, { method: method, url }))
}
_requestMethodWithData(method: Method, url: string, config?: AxiosRequestConfig, data?: any) {
return this.request(Object.assign(config || {}, { method: method, url, data }))
}
}
实例方法的操作主要处理下面的这些类型的请求
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' })
4、/axios.ts
合并/core/Axios.ts和/core/dispatch.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
5、程序入口文件
import axios from './axios'
export * from './types'
export default axios
这里只是粘贴了一些主要模块的代码片段,至于其详细的代码,请查看我们的代码仓库。