[ 造轮子 ] 手动封装 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 的形式处理数据也可以使用回调函数
})
复制代码

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

查看所有标签

猜你喜欢:

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

优秀网站设计

优秀网站设计

林奇 (Patrick J.Lynch)、霍顿 (Sarah Horton) / 机械工业出版社 / 2012-10-1 / 69.00元

优秀网站设计:打造有吸引力的网站(原书第3版),ISBN:9787111399599,作者:(美)Patrick J. Lynch Sarah Horton 著,李静等译一起来看看 《优秀网站设计》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具