封装ajax请求的两种方式

ajax请求能够说是在前端开发工做中必不可少的一个东西html

一、ajax请求原理前端

  • ajax技术的核心是XMLHttpRequest对象,其经过建立一个XMLHttpRequest对象,利用对象的open方法发送请求,判断对象中readyState属性(请求、响应过程的当前活动阶段)的值和status(Http的响应状态)的值,获得responseText
  • open方法的三个参数
    • 第一个参数:请求方式
    • 第二个参数:请求的URL
    • 第三个参数:是否异步(大多数使用异步请求)
  • readyState属性的值
    • 0:未初始化。还没有调用open()方法
    • 1:启动。已经调用open()方法,但还没有调用send()方法。
    • 2:发送。已经调用send()方法,但还没有接收到响应。
    • 3:接收。已经接收到部分响应数据。
    • 4:完成。已经接收到所有响应数据,并且已经能够在客户端使用了。
  • status属性的值
    • 成功:(>= 200 && < 300) || === 304
    • 失败:除去成功的状况

二、jQuery、小程序风格的ajax请求封装 jQuery风格的代码样式node

$.ajax({
    url: xxx,
    success: () =>  {},
    fail: err => {},
})
复制代码

由上,咱们知道在封装函数的时候,首先,它接收的是一个对象,因此,第一步:ajax

const ajax = ({}) => {}
复制代码

接下来,咱们定义对象内部须要接收的参数,有:url(请求的地址)、data(发送的数据)、method(请求方式)、header(请求头部信息)、success(请求成功回调函数)、fail(请求失败回调函数)、async(请求是否异步)、timeout(设置请求超时时间)、onTimeOut(超时处理回调函数)、...小程序

const ajax = ({
        url,
        data = {},
        method = 'get', // 默认为'get'请求
        header,
        async = true, // 默认为异步请求
        timeout,
        success,
        fail,
    }) => {}
复制代码

咱们一般会使用get请求向服务器查询一些信息,也一般会在请求的url后面拼接上数据,像这样:api

http://www.baidu.com?a=b&c=d...
复制代码

那咱们如何实现呢?咱们定义一个拼接url函数,须要两个参数,一个是自己的url,另一个是向后台发送的数据param,因此:promise

// 数据拼接url
    addURL = (url, param) => {
        if(param && Object.keys(param).length) { // 数据不为空
            // 判断url后添加的字符是'?'仍是'&'
            url += (url.indexOf('?') === -1 ? '?' : '&');
            // 拼接数据
            Object.keys(param).map(key => {
                url += `${key}=${param[key]}`
            })
        }
    	return url; 
    }
复制代码

一般呢,咱们使用get方法会遇到查询字符串格式错误的问题,因此,这时须要咱们用encodeURIComponent()进行编码,上面代码改变以下:浏览器

addURL = (url, param) => {
        if(param && Object.keys(param).length) {
            url += (url.indexOf('?') === -1 ? '?' : '&');
            Object.keys(param).map(key => {
                url += encodeURIComponent(key) + '=' + encodeURIComponent(param[key])
            })
        }
    }
复制代码

若是使用的是post方法,咱们只须要将数据给服务端传递过去,最终,咱们写出了封装后的代码:bash

const ajax = ({
        url,
        data = {},
        method = 'get', // 默认为'get'请求
        header,
        async = true, // 默认为异步请求
        timeout = 60 * 1000, //默认60s
        success,
        fail,
    }) => {
       const requestURL = method === 'get' ? this.addURL(url, data) : url;
       const sendData = method === 'get' ? null : data;
       const xhr = new XMLHttpRequest();
       if(header && Object.keys(header).length) {
           Object.keys(header).map(key => {
               xhr.setRequestHeader(key, header[key]);
           })
       }
       xhr.onreadystatechange = () => {
           if(xhr.readyState === 4) {
            try {
                if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
                   const response = xhr.responseText;
                   success(response);
               } else {
                   const error = xhr.status + xhr.statusText;
                   fail(error);
               }
            } catch (ex) {
                
            }
           }
       }
       xhr.open(method, requestURL, async);
       xhr.timeout = timeout;
       xhr.ontimeout = () => {
           console.log('timeout');
       }
       xhr.send(sendData);
    }
    // 拼接url
    addURL = (url, param) => {
        if(param && Object.keys(param).length) {
            url += (url.indexOf('?') === -1 ? '?' : '&');
            Object.keys(param).map(key => {
                url += encodeURIComponent(key) + '=' + encodeURIComponent(param[key])
            })
        }
        return url;
    }
复制代码

上述代码中,利用了try-catch语句。这是由于当请求在指定时间内没有返回,就会自动终止,请求终止后,会调用ontimeout事件处理程序,若是readyState已经变为4,就会调用onreadystatechange事件处理程序,这种状况下,会在请求终止后再次访问status属性,就会致使浏览器报告错误,因此,为了不错误,咱们将status属性语句封装在try-catch语句中。服务器

下面,咱们验证一下,咱们封装好的函数是否可用

// html
    <button id="ajax_btn">ajax请求</button>
    // js
    const ajax_btn = document.getElementById('ajax_btn');
    ajax_btn.onclik = () => {
        ajax({
            url: 'http://localhost:3001/123',
            data: {},
            header: {},
            timeout: 20 * 1000,
            success: res => {
                console.log(res);
            },
            fail: err => {
                throw err;
            }
        });
    };
复制代码

页面样式

点击按钮,就会发送请求,接下来,是见证奇迹的时刻:

咱们成功得到了服务端返回的数据(服务端是本身用node写的一个极其简易的接口)

三、promise风格的ajax请求封装

promise风格的代码

ajax.get('/api').then(res => {}).catch(err => {}).finally();
复制代码

定义封装ajax函数须要的参数

const ajax = ({
        url,
        data,
        method = 'get',
        header,
        async = true,
        timeout = 60 * 1000,
    }) => {};
复制代码

接下来,按照jQuery风格的思路,进行封装

const ajax = ({
        url,
        data = {},
        method = 'get', // 默认为'get'请求
        header,
        async = true, // 默认为异步请求
        timeout = 60 * 1000, //默认60s
    }) => {
        return new Promise((resolve, reject) => {
            const requestURL = method === 'get' ? this.addURL(url, data) : url;
            const sendData = method === 'get' ? null : data;
            const xhr = new XMLHttpRequest();
            if(header && Object.keys(header).length) {
                Object.keys(header).map(key => {
                   xhr.setRequestHeader(key, header[key]);
                })
            }
            xhr.onreadystatechange = () => {
                if(xhr.readyState === 4) {
                    try {
                        if((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
                           const response = xhr.responseText;
                           resolve(response);
                        } else {
                           const error = xhr.status + xhr.statusText;
                           reject(error);
                        }
                    } catch (ex) {
                        // 
                    }
                }
            }
            xhr.open(method, requestURL, async);
            xhr.timeout = timeout;
            xhr.ontimeout = () => {
                console.log('timeout');
            }
            xhr.send(sendData);
        })
    }
    // 拼接url
    addURL = (url, param) => {
        if(param && Object.keys(param).length) {
            url += (url.indexOf('?') === -1 ? '?' : '&');
            Object.keys(param).map(key => {
                url += encodeURIComponent(key) + '=' + encodeURIComponent(param[key])
            })
        }
        return url;
    }
    // get请求
    ajax.get = (url, data) => {
        return ajax({
            url,
            data,
        })
    }
    // post请求
    ajax.post = (url, data) => {
        return ajax({
            url,
            data,
            method = 'post',
        })
    }
复制代码

下面,咱们验证一下封装好的promise风格的ajax函数的可用性

// html
    <button id="ajax_btn">ajax请求</button>
    // js
    const ajax_btn = document.getElementById('ajax_btn');
    ajax_btn.onclick = () => {
        ajax.get('http://localhost:3001/test')
            .then(res => {
                console.log(res);
            })
            .catch(err => {
                throw err;
            })
            .finally(console.log('finally'))
    }
复制代码

点击按钮

当咱们将 get请求换成 post请求

至此,咱们成功地封装了 promise风格的 ajax请求函数

四、优化

IE中,XHR对象是经过MSXML库中的ActiveX对象实现的。因此,在IE中,可能会有三种不一样版本的XHR对象(MSXML2.XMLHttpMSXML2.XMLHttp.3.0MSXML2.XMLHttp.6.0),若是咱们要使用库中的XHR对象,就须要编写一个函数(只适用于IE7之前的版本)

function createXHR() {
        if (typeof arguments.callee.activeXString !== 'string') {
            const versions = ['MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'];
            for(let i = 0; i< versions.length; i++) {
                try {
                    new ActiveXObject(versions[i]);
                    arguments.callee.activeXString = versions[i];
                    break;
                } catch (ex) {
                    // 跳过
                }
            }
        }
        return new ActiveXObject(arguments.callee.activeXString);
    }
复制代码

若是想要支持IE7以上的版本,只须要在上述函数中加入对原生XHR对象的支持,即:

function createXHR() {
        if (typeof XMLHttpRequest !== 'undefined') {
            return new XMLHttpRequest();
        } else if(typeof ActiveXObject !== 'undefined') {
            if(typeof arguments.callee.activeXString !== 'string') {
                const versions = ['MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'];
                for(let i = 0; i< versions.length; i++) {
                    try {
                        new ActiveXObject(versions[i]);
                        arguments.callee.activeXString = versions[i];
                        break;
                    } catch (ex) {
                        // 跳过
                    }
                }
            }
            return new ActiveXObject(arguments.callee.activeXString);
        } else {
            throw new Error('no XHR object available');
        }
    }
复制代码

经过这样的方式,咱们能够直接建立XHR对象:

const xhr = new createXHR();
复制代码

效果:

相关文章
相关标签/搜索