优化思考,我们是否需要axios,自己封装实现ajax,ts书写方式

1,919 阅读2分钟

缘由

其实工作久了,就会有很多思考。作为一个前端,那么我们其实接触最多的除了js代码之外,和业务相关的就是api了。

而作为一个有追求的前端,你是否想过写了这么多年,自己造过几个轮子。其中api,我们每次都是拿别人封装好的。为的只是快,使用,是否想过我们是否需要他。引入别人第三方的东西除了给你项目带来便捷,还有什么呢?

如果你是一个C端(追求性能,seo,优化)的项目,那么你肯定想过如何去优化你的代码。

但是:最好的优化是做减法

回到正题:我们是否需要axios,或者jquery?

当初jquery被干掉的核心原因是angularJS的出现,开启了前端渲染页面的开端。但是jquery在现在依旧存在,并且我在18年转行做前端的时候也依旧使用过。但是我不再使用$('class或者ID'),我只用ajax这个功能。后来出现了axios,为什么用axios呢?因为axios很小,占用资源小。

后来我想着自己封装一个http请求函数。再后来我比较了axios(12kb)和jquery(90kb),可是我们在使用的时候只用了很小的一部分资源。是否真的需要呢?这个就见仁见智了。

我今天的主要目的是为了,自己去了解,并且未来项目中,自己慢慢的去使用这个自己的封装的函数。不觉得12kb依旧很大吗?

一、了解XMLHttpRequest

这个api虽然整体比较复杂,但是伴随前端近20年(更早的时候不是这个名字,不要纠结)。ajax的出现是前端崛起的里程碑式意义。

MDN参考链接:developer.mozilla.org/zh-CN/docs/…

然后这个api定义的确实麻烦,而我们的习惯是json方式的传值,也就是jquery时代的ajax

具体文档大家自己看,我们其实核心需要使用的是

let xhr = new XMLHttpRequest()

// 请求结束

xhr.onloadend

// 请求出错

xhr.onerror

// 请求超时

xhr.ontimeout

// 打开请求

xhr.open(method as string, url as string, true)

// 设置期望的返回数据类型

xhr.responseType = ‘json’

// 设置请求头

xhr.setRequestHeader(key, header[key])

//发送请求

xhr.send(null)

在ie10版本以上,我们只需要关注onloadend部分就行了。请求成功之后就会把参数返回,on开头的是回调函数

可以看到,代码非常简单,而我们平常使用的其实可能就只要这么一点就够了。不过原生的去写,肯定很麻烦。

所以大家都会自己再封装一次

这里奉上简书的一个教程,非常不错:XMLHttpRequest—必知必会

二、是否要用ts

其实我也考虑良多,最后其实没想明白。但是还是用了。

三、成品代码

项目git地址:github.com/ht-sauce/dh…

其中未编译打包的是在src下面的Ajax.ts,打包之后的在Ajax目录下面,其中index.js文件是压缩并打包之后的,是可以直接通过script标签引入使用。

// 该版本对于非ie支持度较为友好,ie情况支持ie10以上
interface Config {
  prefix?: string // 前缀
  url?: string // 路径
  data?: any // 传入的参数
  method?: string
  splicing?: boolean // 当get请求下是否拼接data数据
  timeout?: number
  type?: string // 数据响应类型
  header?: object | any
}
// 埋点上报文件
class Ajax {
  // 私有变量部分
  private _xhr: XMLHttpRequest
  constructor() {
    this._xhr = {} as XMLHttpRequest
  }
  // 终止接口请求
  abort() {
    this._xhr && this._xhr.abort()
  }
  // get情况下需要拼接字符格式
  private splicing(data: any): string {
    let params = ''
    for (const key in data) {
      params += `${key}=${data[key]}&`
    }
    params = '?' + params.substring(0, params.length - 1)
    return params
  }
  // 传入参数预处理
  private config({
    prefix = '',
    url = '',
    data = {},
    method = 'get',
    splicing = true,
    timeout = 1000 * 30, // 默认等待1分钟
    type = 'json',
    header = {
      'Content-Type': 'application/json; charset=UTF-8',
    },
  }: Config): Config {
    method = method.toUpperCase() // 统一转换为大写
    url = prefix + url
    if (method === 'GET' && splicing) url = url + this.splicing(data)

    const config = {
      prefix,
      url,
      data,
      splicing,
      method,
      timeout,
      type,
      header,
    }

    return config
  }
  // image请求方式
  image(config: Config): void {
    const setting: Config = this.config(config)

    let image: HTMLImageElement | null
    image = new Image()
    image.src = setting.url as string

    image = null // 清空内存
  }
  // xhr请求方式
  request(config: Config): Promise<any> {
    const { url, method, timeout, header, type } = this.config(config)

    let xhr: XMLHttpRequest
    if (window.XMLHttpRequest) xhr = new XMLHttpRequest()
    this._xhr = xhr!

    // 返回promise对象
    return new Promise((resolve, reject) => {
      if (!xhr) reject('版本不支持')
      // 请求成功回调函数
      // xhr.onload = e => {
      //   //console.log('load', e)
      //   resolve(e)
      // }
      // 请求结束
      xhr.onloadend = e => {
        const status = xhr.status
        let data
        if (xhr.responseType === 'text') {
          data = xhr.responseText
        } else if (xhr.responseType === 'document') {
          data = xhr.responseXML
        } else {
          data = xhr.response
        }
        resolve({ data, status, xhr: e })
      }
      // 请求出错
      xhr.onerror = e => {
        //console.error('error', e)
        reject(e)
      }
      // 请求超时
      xhr.ontimeout = e => {
        //console.error('timeout', e)
        reject(e)
      }
      // 当 request 被停止时触发
      // xhr.onabort = e => {
      //   reject(e)
      // }

      xhr.open(method as string, url as string, true)

      // 设置期望的返回数据类型
      xhr.responseType = type as XMLHttpRequestResponseType
      // 设置请求头
      for (const key of Object.keys(header)) {
        xhr.setRequestHeader(key, header[key])
      }

      xhr.send(null)
      xhr.timeout = timeout as number
    })
  }
  // 新一代fetch方式,考虑之后不做逻辑封装,业务封装更直接
  // fetch() {}
  // _success() {}
  // _error() {}
}

export default Ajax

使用:

const ajax = new Ajax()
interface Ceshi {
  ceshi: string
  dht: number
}
const one = () => {
  ajax
    .request({
      url: '/test/one',
      data: {
        ceshi: '11111',
        dht: 12321,
      } as Ceshi,
    })
    .then(e => {
      console.log(e)
    })
    .catch(e => {
      console.log(e)
    })
}

四、题外话fetch

MDN地址:developer.mozilla.org/zh-CN/docs/…

其实如果你的项目没有什么ie的需求,就用fetch,挺好,封装只要根据自身业务进行封装就行了。

其实写这个,除了自己一直想实现之外,还有就是对rollup的学习使用。

还有就是小炫耀,我发现nest有点简单。写这个做测试的时候花了半小时用nest写了下接口。