简单的AJAX封装

181天前 · 代码 · 293次阅读

最近写主题的时候遇到了这个需求,为了优化用户体验,需要使用AJAX来提交评论。以前都是在网上CV别人的代码修修补补 ,这次想着借此机会巩固一下自己学习到的基础知识 ,于是就小小地尝试了一番

基本流程

原生JS的AJAX请求主要是通过XMLHttpRequest这个对象实现的。首先来看一下AJAX请求的基本流程:

  1. 创建XMLHttpRequest对象
  2. 设置请求方式、地址和数据
  3. 发送请求
  4. 监听状态变化
  5. 接受数据

根据以上五步,翻阅MDN文档时我们可以很轻松地找到我们需要的属性和方法。

1.创建XMLHttpRequest对象

XMLHttpRequest有一个构造函数XMLHttpRequest(),我们可以直接new一个XMLHttpRequest示例对象出来

let xhr = new XMLHttpRequest();

2.设置请求参数

XMLHttpRequest.open()方法用于初始化一个请求,我们就是使用这个方法来设置我们的参数,该方法接受的参数如下:

  • method -> 请求使用的HTTP方法(GET, POST, ...)
  • url -> 请求的目标url
  • async -> 是否继续异步操作(bool)
  • user -> 用于认证的用户名
  • password -> 用户认证的密码

其中,后面三个参数为可选参数,毕竟不是每一个http请求都需要认证,userpassword的缺省值都是null。而async的缺省值为true。也就是说XHR请求默认就是异步的。为什么不同步呢?因为由于Javascript是单线程的,如果在主线程上发送同步XHR请求结果卡住了,会导致整个线程堵塞。MDN上给出的解释也是如此

注意:主线程上的同步请求很容易破坏用户体验,应该避免;实际上,许多浏览器已完全弃用主线程上的同步XHR支持。

到这里就比较清晰了,我们想要对目标url发送一个请求时,只需要这样简单配置一下。

xhr.open('POST', url);
xhr.open('GET', url);

3.设置状态监听的方法

因为要在发送请求后监听状态变化,并根据不同的状态以此进行不同的处理。再发送之前我们需要先设置好状态变更时的回调函数。而XMLHttpRequest对象有着这样一个属性:onreadystatechange,MDN上的描述是这样子的

readyState 属性发生变化时,调用的 event handler

也就是说我们可以这样子传入一个回调函数

xhr.onreadystatechange = function() {
  console.log('yo! 状态变化了');  
};

那么我们怎么知道我们已经完成了一次请求,并且可以接受数据了呢?答案就在readyState属性里头,通过读取XMLHttpRequest.readyState我们可以判断当前的请求状态

状态描述
0UNSENT代理被创建,但尚未调用 open() 方法。
1OPENEDopen() 方法已经被调用。
2HEADERS_RECEIVEDsend() 方法已经被调用,并且头部和状态已经可获得。
3LOADING下载中; responseText 属性已经包含部分数据。
4DONE下载操作已完成。

那么,我们只需要在回调函数中加入一个简单的判断,就可以知道现在是啥情况了。

xhr.onreadystatechange = function() {
    if(xhr.readyState === 4) {
        console.log('请求已经完成辣!');
    }
};

既然请求已经完成,我们至少需要知道他的HTTP返回码和返回内容,而这些东西我们可以通过XMLHttpRequest.statusXMLHttpRequest.responseText获取。

xhr.onreadystatechange = function() {
    if(xhr.readyState === 4) {
        console.log('返回的状态码为:', xhr.status);
        console.log('返回的文本为', xhr.responseText);
    }
};

我们也可以根据返回的状态码去写不同的回调函数。例如,当返回2xx时,调用成功的回调函数;返回4xx,5xx时,调用失败的回调函数。举个最简单的例子

xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
        if (xhr.status === 200)
            callSuccess(xhr.responseText);
        else
            callError(xhr.responseText);
    }
}

4.发送!

一切都准备就绪了,就差发送按钮发射了 。XHR请求的发送也很简单,只需要调用send()方法即可。

xhr.send();

接下来就交给XHR自己去跑吧~

5.封装

总结上述的知识,我们可以对xhr请求的GETPOST进行简单的封装。

let Ajax = {
    get: function (url, resolve, reject) {
        let xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200)
                    resolve(xhr.responseText);
                else
                    reject(xhr.responseText);
            }
        }
        xhr.send();
    },
    post: function (url, data, resolve, reject) {
        let xhr = new XMLHttpRequest();
        xhr.open('POST', url, true);
        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200)
                    resolve(xhr.responseText);
                else
                    reject(xhr.responseText);
            }
        }
        xhr.send(data);
    }
}

通过这样的封装,我们可以更加方便地发送一个HTTP请求,就像这样:

Ajax.get('https://i.exia.xyz', (success) => {
    console.log('成功了!返回结果为:', JSON.parse(success));
}, (error) => {
    console.log('哎呀,出错了,', JSON.parse(error));
});

image-20211116173828814

不过,这个样子好像还是差了点啥。说起异步,好像总得扯到Promise。没错,还得给他用Promise封装一下。稍加改造,我们就可以得到下面的代码

let Ajax = {
    get: function (url, resolve, reject) {
        new Promise((rs, rj) => {
            let xhr = new XMLHttpRequest();
            xhr.open('GET', url, true);
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200)
                        rs(xhr.responseText);
                    else
                        rj(xhr.responseText);
                }
            }
            xhr.send();
        }).then(success => {
            resolve(success);
        }).catch(error => {
            reject(error);
        });
    },
    post: function (url, data, resolve, reject) {
        new Promise((rs, rj) => {
            let xhr = new XMLHttpRequest();
            xhr.open('POST', url, true);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200)
                        rs(xhr.responseText);
                    else
                        rj(xhr.responseText);
                }
            }
            xhr.send(data);
        }).then(success => {
            resolve(success);
        }).catch(error => {
            reject(error);
        });
    }
}

真不戳,加上了Promise感觉可靠性都高了不少。不过仔细一看,又有点说不上来的奇怪,我Promise不应该是返回一个Promise对象方便之后的链式调用吗。嘿!这才对!再次稍加改造,就可以得到下面这个Ajax对象

let Ajax = {
    get: function (url) {
        return new Promise((rs, rj) => {
            let xhr = new XMLHttpRequest();
            xhr.open('GET', url, true);
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200)
                        rs(xhr.responseText);
                    else
                        rj(xhr.responseText);
                }
            }
            xhr.send();
        });
    },
    post: function (url, data) {
        return new Promise((rs, rj) => {
            let xhr = new XMLHttpRequest();
            xhr.open('POST', url, true);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200)
                        rs(xhr.responseText);
                    else
                        rj(xhr.responseText);
                }
            }
            xhr.send(data);
        });
    }
}

这个时候,调用的方式就需要有所变化了,经典的Promise链式玩法就出现辽。

Ajax.get('https://i.exia.xyz').then(success => {
    console.log('成功了!返回结果为:', JSON.parse(success));
}).catch(error => {
    console.log('哎呀,出错了,', JSON.parse(error));
})

这个时候的功能基本上已经完成了,要说的话,可以再完善一下请求成功的判断条件

if(xhr.status === 200)
//改为
if(xhr.status >= 200 && xhr.status < 300)

也可以再在调用resolve()或者reject()的时候更好地传递一下参数

rs({
    code: xhr.status,
    response: xhr.responseText
});

这样子,可能就能避免一些后端的奇妙操作带来的后果
你认真的吗

以上,就是这次我尝试封装一个简单Ajax对象的过程,代码如果有什么问题欢迎大家指出~

👍 7

js AJAX

最后修改于181天前

评论

贴吧 狗头 原神 小黄脸
收起

贴吧

狗头

原神

小黄脸

  1. a 112天前

    直接fetch不好吗,干嘛自己封xhr呢

    1. 季悠然 109天前

      简单来说就是刚刚学到一些东西想着怎么也给用一下的感觉

目录

avatar

季悠然

寻找有趣的灵魂

127

文章数

1954

评论数

3

分类

好热啊

arknights!

错了就错了吧,一个做错的英雄,至少比什么都不做的笨蛋好。

107