模拟axios利用promise封装一个自己的Ajax库。

一、基础框架

封装Ajax库之前,我们要将其框架结构写出来。

我们通过一个匿名函数,将我们的核心函数暴露给全局。

对这个核心函数进行方法(get、post等)的添加。

(function anonymous(window) {
    //默认配置项
    let _default = {
        // 请求方式
        method: "GET",
        // URL
        url: "",
        // URL基
        baseURL: "",
        // 请求头
        headers: {},
        // 设置返回格式
        dataType: "JSON",
        // POST请求的数据
        data: null,
        // GET请求的数据
        params: null,
        // 缓存
        cache: true,
    };
    // 定义函数
    let ajaxPromise = function ajaxPromise() {};
    // GET方法
    ajaxPromise.get = function (url, options) {};
    // POST方法
    ajaxPromise.post = function (url, data, options) {};
    // 暴露默认配置
    ajaxPromise.defaults = _default;
    // 将函数暴露给window
    window.ajaxPromise = ajaxPromise;
})(window);

二、 管理Ajax请求

这一步来定义发送Ajax请求。即创建XMLHttpRequest核心对象,并发送请求。

请求发出后,根据定义的dataType进行对返回数据的处理。

let ajaxPromise = function ajaxPromise(options) {
    // options中融合了 默认配置信息,用于基于defaults修改的信息、用户执行GET/POST方法时传递的配置信息。越靠后,优先级越高
    let {
        url,
        baseURL,
        method,
        data,
        params,
        dataType,
        headers,
        cache,
    } = options;
    // 基于promise设计模式管理Ajax请求
    return new Promise((resolve, reject) => {
        // 创建核心对象
        let xhr = new XMLHttpRequest();
        // 发送请求
        xhr.open(method, baseURL + url);
        // 监听请求
        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
                // 如果成功则执行
                if (/^(2|3)\d{2}$/.test(xhr.status)) {
                    // 更加dataType处理返回结果
                    let result = xhr.responseText;
                    dataType = dataType.toUpperCase();
                    // 根据设置类型进行对数据的处理
                    switch (dataType) {
                        case "JSON":
                            result = JSON.parse(result);
                            break;
                        case "XML":
                            result = xhr.responseXML;
                            break;
                    }
                    resolve(result, xhr);
                    return;
                }
                // 失败执行
                reject(xhr.statusText, xhr);
            }
        };
        // 发送请求数据
        xhr.send(data);
    });
};

在发送请求时,设置请求头。

// 发送请求
xhr.open(method, baseURL + url);
// 如果headers存在,设置请求头
if (headers !== null && typeof headers === "object") {
    for (let attr in headers) {
        if (headers.hasOwnProperty(attr)) {
            // 处理中文
            let value = headers[attr];
            if (/[\u4e00-\u9fa5]/.test(value)) {
                // 包含中文,将其编码为非中文
                value = encodeURIComponent(value);
            }
            xhr.setRequestHeader(attr, value);
        }
    }
}

三、 根据请求方式处理传入参数

主要根据请求方式进行传递参数的处理。

// 处理传递的参数
if (/^(GET|DELETE|HEAD|OPTIONS)$/i.test(method)) {
    // GET系列的
    if (params) {
        url += `${ajaxPromise.check(url)}${ajaxPromise.formateDate(params)}`;
    }
    // 判断是否缓存
    if (cache === false) {
        url += `${ajaxPromise.check(url)}_=${new Date()}`;
    }
    // GET系列请求主体就是什么都不放
    data = null;
} else {
    // POST系列
    if (data) {
        data = ajaxPromise.formateDate(data);
    }
}
return new Promise((resolve, reject) => {
    // code...
}

四、 封装方法

每一种请求方式实际上对应一个函数,因此我们可以将方法的定义修改为如下:

// GET系列
["get", "delete", "head", "options"].forEach((item) => {
    ajaxPromise[item] = function anonymous(url, options) {
        options = {
            //   默认值
            ..._default,
            // 用户调取方法传递的配置
            ...options,
            // 请求的URL地址(第一个参数)
            url: url,
            //
            method: item.toUpperCase(),
        };
        return ajaxPromise(options);
    };
});
// POST系列
["post", "put", "patch"].forEach((item) => {
    ajaxPromise[item] = function anonymous(url, data = {}, options = {}) {
        options = {
            //   默认值
            ..._default,
            // 用户调取方法传递的配置
            ...options,
            // 请求的URL地址(第一个参数)
            url: url,
            //
            method: item.toUpperCase(),
            data: data,
        };
        return ajaxPromise(options);
    };
});

image-20200907184328385

五、 正常使用

封装好我们的Ajax库之后,可以使用一言接口简单的测试一下。

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <script src="ajaxPromise.js"></script>
    </head>

    <body>
        <script>
            ajaxPromise.get('https://v1.hitokoto.cn/').then(result => {
                console.log(result.hitokoto);
                document.write(`<p>${result.hitokoto}</p>`)
            })
        </script>
    </body>

</html>

image-20200907184727633