[ 造轮子 ] 手动封装 AJAX (三) —— 最终版

栏目: jQuery · 发布时间: 5年前

内容简介:代码如下代码如下需要注意的是返回数据格式可以设置这几个值,之后会写一个详细的传参指南
  • 首先打开一个连接
  • 发送数据
  • 返回结果

我们要自定义的设置有哪些

  • 设置请求方式
  • 设置请求头
  • 设置返回数据格式
  • 返回成功后或失败后

我们要做的功能有哪些

  • 数据校验
  • 统一数据的格式
  • 支持文件上传
  • 对于传入参数的容错处理

经过以上思考基本结构大致成型

  1. 数据校验
  2. 数据格式的统一
  3. 建立连接
  4. 设置请求头
  5. 设置返回数据格式
  6. 发送数据
  7. 返回成功或失败

代码如下

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('如果设置了请求方式为同步,即使设置了返回数据格式也不会生效');
    }
};
复制代码

需要注意的是返回数据格式可以设置这几个值,之后会写一个详细的传参指南

[ 造轮子 ] 手动封装 AJAX (三) —— 最终版

接下来是数据的处理

  • 首先我们需要保证请求格式,不管传入时是大写还是小写,在我们设置请求格式时要是全部大写
  • 还有就是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}`;
        }
   }
};
复制代码

这里详细说说对需要发送数据的处理,按照序号来说

  1. 判断它不是 formData ,也就是说是 GET 和 POST 时我们进行数据处理,是 formData 不进行处理,直接发送,这是为了能够实现文件上传功能
  2. 判断它是不是 {key:vlue} 这种格式的,是的话解析或拼接,不是的话跳到 [7] 如果是字符串直接加到 url 后边
  3. [3] [4] [5] 这里是为了处理成 url?key=value$key=value 这种 url 传参的数据格式
  4. [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 的形式处理数据也可以使用回调函数
})
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

JavaScript精粹

JavaScript精粹

爱德华兹 / 高铁军 / 人民邮电出版社 / 2007-6 / 49.00元

《JavaScript 精粹》主要介绍JavaScript应用中一些常见的问题及其解决方法,从最基础的数字、字符串、数组到进阶的DOM、表单验证、cookie,再到较为高级的Ajax,书中均有涉及。《JavaScript 精粹》覆盖现在非常流行和通用的技术,提出很多出现频率较高的Web开发常见问题,并提供了大量的技巧和解决方案,具有很强的实用性和通用性,书中的代码也具有很强的兼容性。《JavaSc......一起来看看 《JavaScript精粹》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

SHA 加密
SHA 加密

SHA 加密工具