首页 > 其他分享 >基于ts重构axios

基于ts重构axios

时间:2022-09-19 01:11:54浏览次数:130  
标签:重构 axios return val request ts headers data any

ustbhuangyi 老师的 基于TypeScript从零重构axios学习记录。

知识点

TypeScript 常用语法:

基础类型函数变量声明接口泛型类型推新高级类型

axios js库:

项目脚手架基础功能实现异常情况处理接口扩展拦截器实现配置化实现取消功能实现其他功能实现等等

主要工具:
JestTSLintCommitizenPrettierRollupJSSemantic release

基本语法

点我

需求分析

Features

  • 在浏览器使用 XMLHttpRequest 对象通讯
  • 支持 Promise API
  • 支持请求和响应的拦截器
  • 支持请求数据和响应数据的转换
  • 支持请求的取消
  • JSON数据的自动转换
  • 客户端防止 XSRF

基于 XMLHttpRequest 编写基本请求代码

处理请求数据:url/body/headers

src/types/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
}

src/xhr.ts

import { AxiosRequestConfig } from './types'

export default function xhr(config: AxiosRequestConfig): void {
  const { data = null, url, method = 'get', headers } = config
  const request = new XMLHttpRequest()
  // method,url,async
  request.open(method.toUpperCase(), url, true)
  Object.keys(headers).forEach(name => {
    if (data === null && name.toLowerCase() === 'content-type') {
      delete headers[name]
    } else {
      request.setRequestHeader(name, headers[name])
    }
  })
  request.send(data)
}

src/index.ts

import { AxiosRequestConfig } from './types'
import { buildURL } from './helpers/url';
import { transformRequest } from './helpers/data';
import xhr from './xhr'
import { processHeaders } from './helpers/header';

function axios(config: AxiosRequestConfig): void {
  processConfig(config)
  xhr(config)
}

function processConfig(config: AxiosRequestConfig): void {
  config.url = transformURL(config)
  config.data = transformRequestData(config)
  config.headers = transformHeaders(config)
}

function transformHeaders(config: AxiosRequestConfig): void {
  const { headers = {}, data } = config
  return processHeaders(headers, data)
}

function transformRequestData(config: AxiosRequestConfig): void {
  return transformRequest(config.data)
}

function transformURL(config: AxiosRequestConfig): string {
  const { url, params } = config;
  return buildURL(url, params)
}

export default axios

工具类

data.ts

import { isPlainObject } from "./util";

export function transformRequest(data: any): any {
  if (isPlainObject(data)) {
    return JSON.stringify(data)
  }
  return data
}

headers.ts

import { isPlainObject } from "./util"

function normalizeHeaderName(headers: any, normalizedName: string): void {
  if (!headers) {
    return
  }
  Object.keys(headers).forEach(name => {
    if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {
      headers[normalizedName] = headers[name]
      delete headers[name]
    }
  })
}

export function processHeaders(headers: any, data: any): any {
  normalizeHeaderName(headers, 'Content-Type')
  if (isPlainObject(data)) {
    if (headers && !headers['Content-Type']) {
      headers['Content-Type'] = 'application/json;charset=utf-8'
    }
  }
  return headers
}

url.ts

import { isDate, isPlainObject } from './util'

function encode(val: string): string {
  return encodeURIComponent(val)
    .replace(/%40/g, '@')
    .replace(/%3A/ig, ':')
    .replace(/%24/g, '**util.ts**

​```tsx
const toString = Object.prototype.toString

export function isDate(val: any): val is Date {
  return toString.call(val) === '[object Date]'
}

export function isPlainObject(val: any): val is Object {
  return toString.call(val) === '[object Object]'
}

处理响应数据

定义响应接口

types/index

export interface AxiosResponse {
  data: any
  status: number
  statusText: string
  headers: any
  config: AxiosRequestConfig
  request: any
}
export interface AxiosPromise extends Promise<AxiosResponse> {

}

处理 headers 的数据

helpers/header.ts

export function processHeaders(headers: any, data: any): any {
  normalizeHeaderName(headers, 'Content-Type')
  if (isPlainObject(data)) {
    if (headers && !headers['Content-Type']) {
      headers['Content-Type'] = 'application/json;charset=utf-8'
    }
  }
  return headers
}

export function parseHeaders(headers: string): any {
  let parsed = Object.create(null)
  if (!headers) {
    return headers
  }
  headers.split('\r\n').forEach(line => {
    let [key, val] = line.split(':')
    key = key.trim().toLowerCase()
    if (!key) {
      return
    }
    if (val) {
      val = val.trim();
    }
    parsed[key] = val
  })
  return parsed
}

处理 响应data

helpers/data.ts

export function transformResponse(data: any): any {
  if (typeof data === 'string') {
    try {
      data = JSON.parse(data)
    } catch (e) {
      // do nothing
    }
  }
  return data
}

修改 xhr, 返回一个 Promise

xhr.ts

import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from './types'
import { parseHeaders } from './helpers/headers'

export default function xhr(config: AxiosRequestConfig): AxiosPromise {
  return new Promise(resolve => {
    const { data = null, url, method = 'get', headers, responseType } = config
    const request = new XMLHttpRequest()
    if (responseType) {
      request.responseType = responseType
    }
    // method,url,async
    request.open(method.toUpperCase(), url, true)

    request.onreadystatechange = function handleLoad() {
      if (request.readyState !== 4) {
        return
      }

      const responseHeaders = parseHeaders(request.getAllResponseHeaders())
      const responseData = responseType && responseType !== 'text' ? request.response : request.responseText
      const response: AxiosResponse = {
        data: responseData,
        status: request.status,
        statusText: request.statusText,
        headers: responseHeaders,
        config,
        request
      }
      resolve(response)
    }
    Object.keys(headers).forEach(name => {
      if (data === null && name.toLowerCase() === 'content-type') {
        delete headers[name]
      } else {
        request.setRequestHeader(name, headers[name])
      }
    })
    request.send(data)
  })

}


具体代码地址

)

.replace(/%2C/ig, ',')
.replace(/%20/g, '+')
.replace(/%5B/ig, '[')
.replace(/%5D/ig, ']')

}

export function buildURL(url: string, params?: any): string {
if (!params) {

return url

}
const parts: string[] = []
Object.keys(params).forEach(key => {

const val = params[key]
if (val === null || typeof val === 'undefined') {
  return
}
let values = []
if (Array.isArray(val)) {
  values = val
  key += '[]'
} else {
  values = [val]
}
values.forEach(val => {
  if (isDate(val)) {
    val = val.toISOString()
  } else if (isPlainObject(val)) {
    val = JSON.stringify(val)
  }
  parts.push( `${encode(key)}=${encode(val)}` )
})

})
let serializedParams = parts.join('&')
if (serializedParams) {

const markIndex = url.indexOf('#')
if (markIndex !== -1) {
  url = url.slice(0, markIndex)
}
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams

}
return url
}

**util.ts**

​```tsx
const toString = Object.prototype.toString

export function isDate(val: any): val is Date {
  return toString.call(val) === '[object Date]'
}

export function isPlainObject(val: any): val is Object {
  return toString.call(val) === '[object Object]'
}


处理响应数据

定义响应接口

types/index

export interface AxiosResponse {
  data: any
  status: number
  statusText: string
  headers: any
  config: AxiosRequestConfig
  request: any
}
export interface AxiosPromise extends Promise<AxiosResponse> {

}

处理 headers 的数据

helpers/header.ts

export function processHeaders(headers: any, data: any): any {
  normalizeHeaderName(headers, 'Content-Type')
  if (isPlainObject(data)) {
    if (headers && !headers['Content-Type']) {
      headers['Content-Type'] = 'application/json;charset=utf-8'
    }
  }
  return headers
}

export function parseHeaders(headers: string): any {
  let parsed = Object.create(null)
  if (!headers) {
    return headers
  }
  headers.split('\r\n').forEach(line => {
    let [key, val] = line.split(':')
    key = key.trim().toLowerCase()
    if (!key) {
      return
    }
    if (val) {
      val = val.trim();
    }
    parsed[key] = val
  })
  return parsed
}

处理 响应data

helpers/data.ts

export function transformResponse(data: any): any {
  if (typeof data === 'string') {
    try {
      data = JSON.parse(data)
    } catch (e) {
      // do nothing
    }
  }
  return data
}

修改 xhr, 返回一个 Promise

xhr.ts

import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from './types'
import { parseHeaders } from './helpers/headers'

export default function xhr(config: AxiosRequestConfig): AxiosPromise {
  return new Promise(resolve => {
    const { data = null, url, method = 'get', headers, responseType } = config
    const request = new XMLHttpRequest()
    if (responseType) {
      request.responseType = responseType
    }
    // method,url,async
    request.open(method.toUpperCase(), url, true)

    request.onreadystatechange = function handleLoad() {
      if (request.readyState !== 4) {
        return
      }

      const responseHeaders = parseHeaders(request.getAllResponseHeaders())
      const responseData = responseType && responseType !== 'text' ? request.response : request.responseText
      const response: AxiosResponse = {
        data: responseData,
        status: request.status,
        statusText: request.statusText,
        headers: responseHeaders,
        config,
        request
      }
      resolve(response)
    }
    Object.keys(headers).forEach(name => {
      if (data === null && name.toLowerCase() === 'content-type') {
        delete headers[name]
      } else {
        request.setRequestHeader(name, headers[name])
      }
    })
    request.send(data)
  })

}


具体代码地址

标签:重构,axios,return,val,request,ts,headers,data,any
From: https://www.cnblogs.com/Scooby/p/16706407.html

相关文章

  • Codeforces Round #316 (Div. 2) D Tree Requests
    TreeRequests判断\(V_i\)的子树中,深度为\(h_i\)的结点上所带有的字符,能否组成一个回文串启发式合并维护所有深度上不同字符的数量,并且维护其奇数字符出现的次数如......
  • Pest24: A large-scale very small object data set of agricultural pests for multi
    1.论文主要工作建立了一个包含24类典型虫害,并且使用了几种最先进的深度学习检测方法(RCNN、SSD、YOLOv3、CascadeR-CNN)来检测数据集中的害虫,从多个方面分析了数据集,发现了......
  • bootstrap5源码解读
    ######################## 001:  (1)@charset "UTF-8":声明该css文件的编码为UTF-8:(2):root{}:在根元素html中声明自定义属性,这样其他任何元素都可使用这些自定义属......
  • 从SAP ECC升级到SAP S4HANA, 几个Key Points
    从SAPECC升级到SAPS4HANA,几个KeyPoints  自从SAP公司的拳头产品S/4HANA横空出世以来,就引起了世界范围内的众多客户以及ERP咨询业界的强烈关注。 笔者发现很......
  • 重构_改善既有代码的设计 pdf
    高清扫描版下载链接:https://pan.baidu.com/s/1pEQBUYRfQKy3HDbtgNZPqQ点击这里获取提取码 ......
  • 关于vue3+ts中使用props进行类型限制报错的问题
    报错Type'{}'isnotassignabletotype'(props:Readonly<Props>)=>object'.  Type'{}'providesnomatchforthesignature'(props:Readonly<Props>):obj......
  • AtCoder Regular Contest 148 C Lights Out on Tree
    挺好的一道题,简单写一下题解吧。首先有挺多很naive的结论:每个节点按两遍等于没按。熄灭所有的灯只有一种方案。其实将灯熄灭的方案无非就是从上往下dfs,如果当前灯......
  • nodeJS中module.exports和exports的区别
      简单说就是,module.exprots是堆内存中的对象,而exports是栈内存中指向module.exprots的引用,实际上exports指向的是堆内存中的module.exprots的堆内存空间,所以需要用......
  • camunda_01_documents
    官方文档camunda官方文档邵晨峰翻译的官网流程引擎系列文章邵晨峰翻译的官方快速入门教程中文版(完整版)分享牛-Camunda教程|Camunda视频专栏博客博客园-我不是大......
  • Vue3 Error on build in CI: Cannot find module ‘node:path‘ in vite.config.ts
    node 版本是v14.17.0的版本,出现了不兼容的问题,于是升级到了稳定版v16.16.0。重新删除一下安装包 rm-rfnode_modulespackage-lock.json 再 npmi,再build就没......