导言
在开始之前先想一想 ajax 是怎样的流程
首先打开一个连接
发送数据
返回结果
我们要自定义的设置有哪些
设置请求方式
设置请求头
设置返回数据格式
返回成功后或失败后
我们要做的功能有哪些
数据校验
统一数据的格式
支持文件上传
对于传入参数的容错处理
经过以上思考基本结构大致成型
数据校验
数据格式的统一
建立连接
设置请求头
设置返回数据格式
发送数据
返回成功或失败
代码如下
class AJAX {
constructor({url = “”,method = “GET”,data = {},async = true,success,error,resType = “”,headers = {}}) {
// 集中管理传递过来的参数
this.option = {url,method,data,async,success,error,resType,headers};
this.xhr = new XMLHttpRequest();
this.start();
}
start() {
// 数据校验
this.checkOption();
// 数据格式的统一
this.initOption();
// 建立连接
this.open();
// 设置请求头
this.setHeaders();
// 设置返回数据格式
this.setResponseType();
// 发送数据
this.sendData()
// 返回成功或失败
this.responseData();
};
}
接下来添加校验功能
首先 url 不能是空
然后请求头必须是字面量对象格式 {key:value}
再有就是一些简单的警告
代码如下
checkOption() {
let {url,async,resType,headers} = this.option;
if (url === ”) {
throw new Error(‘ 请求地址不能为空 ’); // 打印错误信息,并停止当前进程
//Console.error(‘ 请求地址为空 ’); 也可以打印错误信息,但是不能停止当前进程
}
if(typeof headers !== ‘object’){
throw new Error(‘ 设置请求头时请传入 {key:value,key:value…} 的格式 ’);
}
if(typeof resType !== ‘string’){
throw new Error(‘ 设置返回数据格式时请传入字符出串格式 ’);
}
if (typeof url !== ‘string’) {
// 输出警告信息
console.warn(‘ 当前请求地址不是字符串,现在将其尝试转换为字符串 ’);
}
if (async === false && resType != ”) {
console.warn(‘ 如果设置了请求方式为同步,即使设置了返回数据格式也不会生效 ’);
}
};
需要注意的是返回数据格式可以设置这几个值,之后会写一个详细的传参指南
接下来是数据的处理
首先我们需要保证请求格式,不管传入时是大写还是小写,在我们设置请求格式时要是全部大写
还有就是 url 可能是数字的,需要转换成字符
为了方便将 async 不是布尔型的转成布尔型,这是什么概念,就是传参时 写数字 1 是异步 数字 0 是同步
将需要发送的内容做一个处理
initOption() {
let {url,async,method} = this.option;
//url 不是字符串转换成字符串
if (typeof url !== ‘string’) {
try {
this.option.url = url.toString();
console.log(` 转换成功: “${this.option.url}”`);
} catch (error) {
throw new Error(‘url 转换字符串失败 ’);
}
}
//async 不是布尔型转成布尔型
if(typeof async !==’boolean’){
async == true ? this.option.async = true : this.option.async = false;
}
// 将 post get 转换为大写
this.option.method = method.toUpperCase();
//post 和 get 数据初始化
if(this.option.method != ‘FORMDATA’){// [1]
let data = this.option.data;
if(typeof data === ‘object’){//[2]
if(this.option.method === ‘GET’){
let arr=[];
for(let name in data){
arr.push(`${name}=${data[name]}`);//[3]
}
let strData=arr.join(‘&’);//[4]
this.option.data=`?${strData}`;//[5]
}else if(this.option.method === ‘POST’){
let formData = new FormData();//[6]
for(let key in data){
formData.append(`${key}`,`${data[key]}`);
}
this.option.data=formData;
}
}else if(typeof data === ‘string’ && this.option.method === ‘GET’){//[7]
this.option.data=`?${data}`;
}
}
};
这里详细说说对需要发送数据的处理, 按照序号来说
判断它不是 formData,也就是说是 GET 和 POST 时我们进行数据处理,是 formData 不进行处理,直接发送,这是为了能够实现文件上传功能
判断它是不是 {key:vlue} 这种格式的,是的话解析或拼接,不是的话跳到 [7] 如果是字符串直接加到 url 后边
[3] [4] [5] 这里是为了处理成 url?key=value$key=value 这种 url 传参的数据格式
[6] 是新建了一个 FormData 对象,是 ajax2.0 里边的,它最主要的可以用 ajax 实现文件上传功能,在这里是为了代码简单
打开连接
经过之前的数据处理这里只需要判断下是 GET 还是其他方式(post formdata),然后选择对应的连接方式
open(){
let {method,url,async,data} = this.option;
if(method === ‘GET’){
this.xhr.open(method,url+data,async);
}else{
this.xhr.open(method,url,async);
}
}
设置自定义请求头
将传入的参数进行解析,然后设置自定义请求头代码如下
setHeaders(){
let headers = this.option.headers;
for(let key in headers){
this.xhr.setRequestHeader(`${key.toString()}`,`${headers[key].toString()}`)
}
}
设置返回数据格式、发送数据
由于同步请求时不能设置返回数据格式,所以做下判断
发送数据这里,在经过之前的数据处理后只有 GET 方式有所区别,其他两种没有区别(支持 GET POST 以及我自己定义的一种,更多请求方法可自行扩展)
setResponseType() {
if (this.option.async) {
this.xhr.responseType = this.option.resType;
}
}
sendData(){
if(this.option.method == ‘GET’){
this.xhr.send();
}else{
this.xhr.send(this.option.data);
}
}
请求完成后的数据返回
请求完成后会返回数据 判断 success 以及 error 是不是函数,是的话会将数据返回给 success 或者将错误信息返回给 error
responseData(){
this.xhr.onload = ()=>{
if(this.xhr.status >= 200 && this.xhr.status < 300 || this.xhr.status === 304){
typeof this.option.success === ‘function’ && this.option.success(this.xhr.response);
}else{
typeof this.option.error === ‘function’ && this.option.error(this.xhr.statusText);
}
}
}
在实现基本功能后,突然想到 jQuery 的 ajax 是会返回一个 promise 对象,可以同时使用回掉函数,或者使用 then 和 catch 来处理数据 因此修改了下传入参数,以及返回数据的处理
传参时代码如下
//add resolve reject
class AJAX {
constructor({url = “”,method = “GET”,data = {},async = true,success,error,resType = “”,headers = {},resolve,reject}) {
this.option = {url,method,data,async,success,error,resType,headers,resolve,reject};
this.xhr = new XMLHttpRequest();
this.start();
}
}
返回数据时代码如下
responseData(){
this.xhr.onload = ()=>{
if(this.xhr.status >= 200 && this.xhr.status < 300 || this.xhr.status === 304){
typeof this.option.success === ‘function’ && this.option.success(this.xhr.response);
this.option.resolve(this.xhr.response);//add
}else{
typeof this.option.error === ‘function’ && this.option.error(this.xhr.statusText);
this.option.reject(this.xhr.statusText);//add
}
}
}
最终代码
class AJAX {
constructor({url = “”,method = “GET”,data = {},async = true,success,error,resType = “”,headers = {},resolve,reject}) {
this.option = {url,method,data,async,success,error,resType,headers,resolve,reject};
this.xhr = new XMLHttpRequest();
this.start();
}
start() {
// 数据校验
this.checkOption();
// 数据格式的统一
this.initOption();
// 建立连接
this.open();
// 设置请求头
this.setHeaders();
// 设置返回数据格式
this.setResponseType();
// 发送数据
this.sendData()
// 返回成功或失败
this.responseData();
};
checkOption() {
let {url,async,resType,headers} = this.option;
if (url === ”) {
throw new Error(‘ 请求地址不能为空 ’); // 打印错误信息,并停止当前进程
//Console.error(‘ 请求地址为空 ’); 也可以打印错误信息,但是不能停止当前进程
}
if(typeof headers !== ‘object’){
throw new Error(‘ 设置请求头时请传入 {key:value,key:value…} 的格式 ’);
}
if(typeof resType !== ‘string’){
throw new Error(‘ 设置返回数据格式时请传入字符出串格式 ’);
}
// “” 与设置为 ”text” 相同,是默认类型(实际上是 DOMString)
// “arraybuffer” 将接收到的数据类型视为一个包含二进制数据的 JavaScript ArrayBuffer
// “blob” 将接收到的数据类型视为一个包含二进制数据的 Blob 对象
// “document” 将接收到的数据类型视为一个 HTML Document 或 XML XMLDocument,这取决于接收到的数据的 MIME 类型
// “json” 将接收到的数据类型视为 JSON 解析得到的
// “text” 将接收到的数据类型视为包含在 DOMString 对象中的文本
if (typeof url !== ‘string’) {
// 输出警告信息
console.warn(‘ 当前请求地址不是字符串,现在将其尝试转换为字符串 ’);
}
if (async === false && resType != ”) {
console.warn(‘ 如果设置了请求方式为同步,即使设置了返回数据格式也不会生效 ’);
}
};
initOption() {
let {url,async,method} = this.option;
//url 不是字符串转换成字符串
if (typeof url !== ‘string’) {
try {
this.option.url = url.toString();
console.log(` 转换成功: “${this.option.url}”`);
} catch (error) {
throw new Error(‘url 转换字符串失败 ’);
}
}
//async 不是布尔型转成布尔型
if(typeof async !==’boolean’){
async == true ? this.option.async = true : this.option.async = false;
}
// 将 post get 转换为大写
this.option.method = method.toUpperCase();
//post 和 get 数据初始化
if(this.option.method != ‘FORMDATA’){
let data = this.option.data;
if(typeof data === ‘object’){
if(this.option.method === ‘GET’){
let arr=[];
for(let name in data){
arr.push(`${name}=${data[name]}`);
}
let strData=arr.join(‘&’);
this.option.data=`?${strData}`;
}else if(this.option.method === ‘POST’){
let formData = new FormData();
for(let key in data){
formData.append(`${key}`,`${data[key]}`);
}
this.option.data=formData;
}
}else if(typeof data === ‘string’ && this.option.method === ‘GET’){
this.option.data=`?${data}`;
}
}
};
open(){
let {method,url,async,data} = this.option;
if(method === ‘GET’){
this.xhr.open(method,url+data,async);
}else{
this.xhr.open(method,url,async);
}
}
setHeaders(){
let headers = this.option.headers;
for(let key in headers){
this.xhr.setRequestHeader(`${key.toString()}`,`${headers[key].toString()}`)
}
}
setResponseType() {
if (this.option.async) {
this.xhr.responseType = this.option.resType;
}
}
sendData(){
if(this.option.method == ‘GET’){
this.xhr.send();
}else{
this.xhr.send(this.option.data);
}
}
responseData(){
this.xhr.onload = ()=>{
if(this.xhr.status >= 200 && this.xhr.status < 300 || this.xhr.status === 304){
typeof this.option.success === ‘function’ && this.option.success(this.xhr.response);
this.option.resolve(this.xhr.response);
}else{
typeof this.option.error === ‘function’ && this.option.error(this.xhr.statusText);
this.option.reject(this.xhr.statusText);
}
}
}
all(promises) {
return Promise.all(promises);
};
}
function ajax({url,method,data,async,success,error,resType,headers}){
return new Promise((resolve, reject) => {
return new AJAX({url,method,data,async,success,error,resType,headers,resolve,reject});
});
}
使用时可以将代码复制粘贴到单独的 js 文件然后用 script 标签引入 也可以添加一行 export 代码将最后的 ajax 暴露出去 使用 import 引入
具体使用方法用法
ajax({
url:’api/login’,
method:’post’,// 支持 GET POST 和我自定义的 FORMDATA,传入时不区分大小写
data = {
name:”yhtx”,
id:”1997″
},// 除了这种还支持字符串 “name=yhtx&id=1997″;以及 formData 数据,在传入 formData 数据时请将 method 设置为 FORMDATA
async = true,// 可以使用数字 1 代替 true,数字 0 代替 false
success(res){
// 可以使用回调的形式处理数据也可以使用 then
},error(err){
// 可以使用回调的形式处理错误也可以使用 catch
},
resType = “”,// 可以传入 “” “arraybuffer” “blob” “document” “json” “text”
headers = {
mycookie: “46afqwiocibQEIJfa498./&678” // 使用对象的方式传参
}
}).then((res)=>{
// 可以使用 then 的形式处理数据也可以使用回调函数
}).catch((err)=>{
// 可以使用 catch 的形式处理数据也可以使用回调函数
})