如何优雅手写实现 ajax

  “ 我们习惯于使用$ajax()、$axios、fetch来帮我们实现http请求数据,我们似乎忘记原生的该怎么写了。

     无论是通过模板引擎加载的,还是前后端分离的项目,我们都可以通过 ajax 来实现页面的无刷新请求数据。比如我们有个实时数据要展示,肯定是页面图表局部刷新,谁也不期待整个页面反复加载。

      ajax 在我们的开发工作中已经司空见惯,几乎所有我们频繁使用的库和框架(如 jQuery、Vue、React)都提供了经过完善封装后的 ajax 方法,我们习惯于使用$ajax()、$axios、fetch来帮我们实现http请求数据,我们似乎忘记原生的该怎么写了。来吧,我们重温原理,温故而知新。

01

手写AJAX的步骤

 

手写ajax并没有一个统一的标准,一般的步骤是分以下几步:

  • 创建XMLHttpRequest对象

  • 打开连接

  • 发送请求

  • 响应

也可以参照w3school: 

    https://www.w3schools.com/xml/ajax_intro.asp

02

步骤简要描述

 

1. 创建XMLHttpRequest对象

    我们常用的 ajax 就是通过 XMLHttpRequest 对象实现的,这个对象有很多的属性和事件,在使用之前我们需要先将它实例化。

let xhr;if ((window as any).XMLHttpRequest) {    xhr = new XMLHttpRequest();} else {    //兼容IE    xhr = new ActiveXObject('Microsoft.XMLHTTP');}

2. 连接与发送

    在通过send方法发送请求后,xhr 对象在收到响应数据时会自动填充到其对应的属性中,xhr 具有以下常用属性:

responseText:请求返回的数据内容
responseXML:如果响应内容是  "text/xml""application/xml",这个属性将保存响应数据的 XML DOM文档

status:响应的HTTP状态,如 200 304 404 等
statusText:HTTP状态说明
readyStatus:请求/响应过程的当前活动阶段
timeout:设置请求超时时间

let method=options.type.toUpperCase();if ('GET'===method) {    xhr.open('get', `${options.url}?${queryString}`, options.async);    xhr.send();} else if ('POST'===method) {    xhr.open('post', options.url, options.async);    xhr.send(options.data);}

3. 接收响应

   readyStatus的值会随着请求各阶段的变化而改变,其一共有 5 个值:

xhr.readyStatus==0 尚未调用 open 方法
xhr.readyStatus==1 已调用 open 但还未发送请求(未调用 send)
xhr.readyStatus==2 已发送请求(已调用 send)
xhr.readyStatus==3 已接收到请求返回的数据
xhr.readyStatus==4 请求已完成

当readyStatus的状态发生改变时,会触发 xhr 的事件onreadystatechange,于是我们就可以在这个方法中,对接收到的数据进行处理

//接收xhr.onreadystatechange = () => {    if (xhr.readyState === 4) {        //clearTimeout(timer);        if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {            resolve(xhr.responseText);        } else {            reject(xhr.status);        }    }}

 

 

03

完整示例代码

 

1. 我们看一下完整代码

     这里函数参数我采用了TypeScript,  TypeScript使我们的代码可维护性和可读性变得非常好,本身的类型系统就是一个最好的文档,我们在编译阶段就可以发现错误。该 ajax 方法通过 我们上一篇文章提到的Promise 方式实现回调。

interface Options {    url: string;    method?: string;    data: any;    timeout?: number;    headers?:object;    async?:boolean;}
function toQueryString(params) {    let dataArr = [];    params.t = Math.random();    for (let key in params) {        dataArr.push(`${key}=${encodeURIComponent(params[key])}`);    }    return dataArr.join('&');}
export function ajax(options: Options={method:'GET',data:{},url:'',timeout:10000,async:true}) {    return new Promise((resolve, reject) => {              if (!options.url) throw Error('the url is required!');
        let xhr;        if ((window as any).XMLHttpRequest) {            xhr = new XMLHttpRequest();        } else {            //兼容IE            xhr = new ActiveXObject('Microsoft.XMLHTTP');        }         //请求参数拼接               const queryString=toQueryString(options.data);        const headers=options.headers||{'Content-Type':'application/json;charset=UTF-8'};                //请求头设置        Object.keys(headers).forEach(key=>{          xhr.setRequestHeader(key,headers[key]);        })                //超时处理        xhr.timeout=options.timeout;        xhr.ontimeout=()=>{          reject('请求超时');        }        //if (options.timeout) {        //    timer = setTimeout(() => {        //        xhr.abort();        //        reject('超时');        //    }, options.timeout)        //}                //连接与发送        let method=options.type.toUpperCase();        if ('GET'===method) {            xhr.open('get', `${options.url}?${queryString}`, options.async);            xhr.send();        } else if ('POST'===method) {            xhr.open('post', options.url, options.async);            xhr.send(options.data);        }            //接收        xhr.onreadystatechange = () => {            if (xhr.readyState === 4) {                //clearTimeout(timer);                if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {                    resolve(xhr.responseText);                } else {                    reject(xhr.status);                }            }        }    });}

 

04

如何使用

 

1. 使用方式     

ajax({    url: 'your request url',    method: 'get',    async: true,    timeout: 1000,    data: {        test: 1,        aaa: 2    }}).then(res => {    console.log('请求成功: ' + res)}).catch(err=>{    console.error('请求失败: ' + err)})

学无止境,觉得不错的话,请关注前端琅琊阁de 微信公众号和头条号!

     

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页