缘由
其实工作久了,就会有很多思考。作为一个前端,那么我们其实接触最多的除了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写了下接口。