Ajax

99 阅读2分钟

ajax就是异步的JavaScript和XML
ajax也就是我们客户端给服务端发送请求以及接受响应的工具

ajax四部曲

  1. 创建ajax实例对象 (XMLHttpRequest)
  2. 连接服务器(open方法)
  3. 发送请求(send方法)
  4. 接受服务器响(onreadystatechange/onload)

ajax的状态:

  • 0 未初始化
  • 1 open方法执行完成
  • 2 send方法执行完成
  • 3 正在解析响应内容
  • 4 交互完成,解析完成

http状态码

  • 1**:消息
  • 2**:成功
  • 3**:重定向(304使用缓存)
  • 4**:客户端错误(403权限不够/404参数错误)
  • 5**:服务器端错误(503过载)

原生js封装Ajax
插件封装都是基于对象做参数,因为对象是无序的,免去参数顺序带来记忆问题
open的三个参数都是可能改变的
get和post提交数据方式不统一的,不一定每次都提交数据
获取接口数据进行渲染

function ajax(options) { //options:配置参数,配置参数可以配置默认值。
    // 判断请求方式
    options.type = options.type || 'get'; //如果传入option.type就使用,否则使用默认的get

    // 接口地址
    if (!options.url) {
        throw new Error('接口地址必须存在'); //抛出错误  new Error生成错误对象,抛出错误。
    }

    // 是否异步
    if (options.async === 'false' || options.async === false) { //同步
        options.async = false;
    } else {
        options.async = true;
    }

    // 判断数据
    if (options.data && Object.prototype.toString.call(options.data) === '[object Object]') { //数据存在,同时是对象格式
        let arr = [];
        for (let key in options.data) {
            arr.push(key + '=' + options.data[key]);
        }
        options.data = arr.join('&');
    }

    // 数据存在,同时数据可以是get和post方式进行发送
    // get方式
    if (options.data && options.type === 'get') {
        options.url += '?' + options.data;
    }
    
    let xhr = new XMLHttpRequest();
    xhr.open(options.type, options.url, options.async);
    // post方式
    if (options.data && options.type === 'post') {
        xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
        xhr.send(options.data);
    } else {
        xhr.send();
    }
    // 获取接口数据
    xhr.onload = function() { //异步数据
        if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) { //请求完成,接口地址正确
            options.success(xhr.responseText)
        } else {
            options.error('接口地址有误');
        }
    }

}

ajax({
    type: 'get', //请求方式
    url: 'abc', //接口地址,必须存在
    data: { //数据
        a: 1,
        b: 2,
        c: 3
    },
    async: 'true', //是否异步
    success: function(res) { //成功获取数据的方法
        console.log(res);
    },
    error: function(err) { //接口失败的方法
        throw new Error(err)
    }
});

Promise
异步编程的一种解决方案,解决异步回调地狱问题
异步编程:定时器、fs文件操作、AJAX、数据库操作等
语法:

new Promise(function (resolve, reject) {
  // resolve 表示成功的回调
  // reject 表示失败的回调
}).then(function (res) {
  // 成功的函数
}).catch(function (err) {
  // 失败的函数
})

三种状态: pending(等待态),fulfilled(成功态),rejected(失败态);状态一旦改变,就不会再变。
promise的静态方法:

  • Promise.all
  • Promise.race
  • Promise.resolve
  • Promise.reject
  • Promise.allSettled
  • Promise.any

Promise封装ajax

   function querystring(obj) {
        if (Object.prototype.toString.call(obj) === '[object Object]') {
            let arr = [];
            for (let key in obj) {
                arr.push(key + '=' + obj[key]);
            }
            return arr.join('&');
        }
        return obj;
    }
    
    // promise封装ajax插件
    function ajax(options = {}) { //设置默认值为空对象 options:配置参数,输入的参数
        // 一.配置默认参数
        const settings = {
            // 1.配置请求方式,默认为get请求
            type: options.type || 'get',
            // 2.配置接口地址,没有默认值
            url: options.url,
            // 3.配置是否异步,默认是异步
            async: options.async || true,
            // 4.配置获取的数据类型,默认字符串
            dataType: options.dataType || 'string',
            // 5.配置数据,默认为空
            data: options.data || '',
            // 6.配置请求头
            headers: {
                ...options.headers
            }

        };
        // 二.检查属性是否符合规范
        // 1.检查type属性,要么属性为空,要么输入get和post,正则
        if (!/^(get|post)$/i.test(settings.type)) {
            throw new Error('目前的请求方式仅支持get和post,其他方式有待更新');
        }

        // 2.检查url,必须填写,不能为空
        if (!settings.url) {
            throw new Error('接口地址必须填写,不能为空');
        }

        // 3.检查是否异步,值必须是布尔值。
        if (!(typeof settings.async === 'boolean')) {
            throw new Error('是否异步的值只能是true和false');
        }

        // 4.检查数据类型dataType
        if (!(/^(json|string)$/i.test(settings.dataType))) {
            throw new Error('获取的数据类型只能是字符串和json对象输出');
        }

        // 5.检查数据格式,只支持字符串和对象
        if (!(typeof settings.data === 'string' || Object.prototype.toString.call(settings.data) === '[object Object]')) {
            throw new Error('传输的数据格式不符合规范,请采用字符串或者对象格式');
        }

        // 6.检查headers格式,格式的值必须是对象。
        const queryFormat = ['application/x-www-form-urlencoded', 'multipart/form-data', 'application/json'];
        if (settings.headers['content-type'] && queryFormat.indexOf(settings.headers['content-type']) === -1) {
            throw new Error('请检查header里面的content-type的值是否正确');
        }


        // 7.如果存在数据,是对象格式,转换成拼接的格式&,判断请求方式是get,拼接在地址栏的后面
        if (settings.data && /^get$/i.test(settings.type)) {
            settings.url += '?' + querystring(settings.data)
        }

        // 三.利用ajax+promise进行参数的应用
        const promise = new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            // alert(1);
            xhr.open(settings.type, settings.url, settings.async);

            // 如果数据存在,同时是post方式,设置请求头,利用send发送
            // 判断是否存在header属性

            // 判断是否存在content-type,获取属性值,添加到请求头里面,post请求
            if (settings.headers['content-type'] && /^post$/i.test(settings.type)) {
                xhr.setRequestHeader('content-type', settings.headers['content-type']);
            }
            // 判断token
            if (settings.headers['authorization']) {
                xhr.setRequestHeader('authorization', settings.headers['authorization']);
            }

            /^post$/i.test(settings.type) ? xhr.send(querystring(settings.data)) : xhr.send()

            xhr.onload = function () {
                if (xhr.readyState === 4 && /^2\d{2}$/.test(xhr.status)) {
                    let res = null;
                    if (settings.dataType.toLowerCase() === 'json') {
                        try {
                            res = JSON.parse(xhr.responseText);
                        } catch (e) {
                            res = xhr.responseText
                        }
                    } else {
                        res = xhr.responseText;
                    }
                    resolve(res);
                } else {
                    reject('接口地址有误')
                }
            };
        })
        return promise;
    }

    // 返回promise对象
    const token = 'fdaskfjdsalfjldksafjlkdsajflasdf';
    ajax({
        type: 'get', //请求类型
        url: 'http://localhost:8888/goods/list', //接口地址
        async: false, //是否异步
        dataType: 'json', //获取数据类型json/string
        data: { //a=1&b=2
            a: 1,
            b: 2,
            c: 3
        },
        headers: { //设置请求头
            'content-type': 'application/x-www-form-urlencoded',
            'authorization': token
        }
    }).then(res => {
        console.log(res);
    }).catch(err => {
        console.log(err);
    });