博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[ 造轮子 ] 手动封装 AJAX (三) —— 最终版
阅读量:6165 次
发布时间:2019-06-21

本文共 11806 字,大约阅读时间需要 39 分钟。

导言

在开始之前先想一想ajax是怎样的流程

  • 首先打开一个连接
  • 发送数据
  • 返回结果

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

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

我们要做的功能有哪些

  • 数据校验
  • 统一数据的格式
  • 支持文件上传
  • 对于传入参数的容错处理
  • 超时处理 //modify time 2019-01-01

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

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

代码如下

class AJAX {    constructor({url = "",method = "GET",data = {},async = true,success,error,resType = "",headers = {}},time = 5000) {        //集中管理传递过来的参数        this.option = {url,method,data,async,success,error,resType,headers,time};        this.timeout;        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,time} = 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 time !== 'number'){        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}`;        }   }};

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

  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);    }    this.timeout = setTimeout(()=>{         typeof this.option.error === 'function' && this.option.error('请求超时,默认超时时间为 5000 毫秒');        this.option.reject('请求超时,默认超时时间为 5000 毫秒');    }, this.option.time);}

请求完成后的数据返回

  • 请求完成后会返回数据

判断 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 rejectclass 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){            clearTimeout(this.timeout);            typeof this.option.success === 'function'  && this.option.success(this.xhr.response);            this.option.resolve(this.xhr.response);//add        }else{            clearTimeout(this.timeout);            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 time !== 'number'){            throw new Error('超时时间请传入数字类型数据');        }                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);        }        this.timeout = setTimeout(()=>{             typeof this.option.error === 'function' && this.option.error('请求超时,默认超时时间为 5000 毫秒');            this.option.reject('请求超时,默认超时时间为 5000 毫秒');        }, this.option.time);    }    responseData(){        this.xhr.onload = ()=>{            if(this.xhr.status >= 200 && this.xhr.status < 300 || this.xhr.status === 304){                clearTimeout(this.timeout);                typeof this.option.success === 'function'  && this.option.success(this.xhr.response);                this.option.resolve(this.xhr.response);//add            }else{                clearTimeout(this.timeout);                typeof this.option.error === 'function' && this.option.error(this.xhr.statusText);                this.option.reject(this.xhr.statusText);//add            }        }    }    all(promises) {        return Promise.all(promises);    };}function ajax({url,method,data,async,success,error,resType,headers,time}){    return new Promise((resolve, reject) => {        return new AJAX({url,method,data,async,success,error,resType,headers,time,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    time = 5000,//请求超时时间,默认为 5000 毫秒    success(res){        //可以使用回调的形式处理数据也可以使用 then    },error(err){        //可以使用回调的形式处理错误也可以使用 catch    },    resType = "",//可以传入 "" "arraybuffer" "blob" "document" "json" "text"        headers = {        mycookie: "46afqwiocibQEIJfa498./&678" //使用对象的方式传参    }}).then((res)=>{    //可以使用 then 的形式处理数据也可以使用回调函数}).catch((err)=>{    //可以使用 catch 的形式处理数据也可以使用回调函数})

转载地址:http://tquba.baihongyu.com/

你可能感兴趣的文章
DEV实现日期时间效果
查看>>
java注解【转】
查看>>
centos 下安装g++
查看>>
下一步工作分配
查看>>
Response. AppendHeader使用大全及文件下载.net函数使用注意点(转载)
查看>>
centos64i386下apache 403没有权限访问。
查看>>
jquery用法大全
查看>>
PC-BSD 9.2 发布,基于 FreeBSD 9.2
查看>>
css斜线
查看>>
Windows phone 8 学习笔记(3) 通信
查看>>
Revit API找到风管穿过的墙(当前文档和链接文档)
查看>>
Scroll Depth – 衡量页面滚动的 Google 分析插件
查看>>
Windows 8.1 应用再出发 - 视图状态的更新
查看>>
自己制作交叉编译工具链
查看>>
Qt Style Sheet实践(四):行文本编辑框QLineEdit及自动补全
查看>>
[物理学与PDEs]第3章习题1 只有一个非零分量的磁场
查看>>
onInterceptTouchEvent和onTouchEvent调用时序
查看>>
android防止内存溢出浅析
查看>>
4.3.3版本之引擎bug
查看>>
SQL Server表分区详解
查看>>