五、重构项目结构

2020-04-24  本文已影响0人  雪燃归来

在前面的几章内容中,我们已经实现了ts-axios的发送请求、处理请求、Promise化等操作,同时也处理错误的处理情况。本片我们还也需要在原先的基础上对项目的代码进行重新排版,对项目的目录结构进行相应的调整。仓库地址:https://github.com/antiai-Antiai/ts-axios

一、改动项目目录的原因

对于项目结构的变动,主要基于下面几点原因:
1、方便使用这使用,对外暴露出的接口要足够简单。
2、在项目的入口文件中除了要到处axios函数,还要导出我们内部定义的一些类型。
3、将核心的代码封装到core目录下,防止不小心篡改。

二、改动后项目的目录结构

image.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

这里只是粘贴了一些主要模块的代码片段,至于其详细的代码,请查看我们的代码仓库。

上一篇 下一篇

猜你喜欢

热点阅读