Ajax&Fetch学习笔记 05、ajax封装(含Promise)

简介: Ajax&Fetch学习笔记 05、ajax封装(含Promise)

一、内部封装js


ajax类
  初始化方法
  绑定响应事件处理
  设置各个header
  发送请求
utils工具类
constant常量类
default配置类:基本的配置属性
index(对外暴露封装类)  给外界调用的接口



①utils工具类


// 添加url请求参数:data为已经序列化的string值,这里只需要判断url后添加?还是&
const addUrlData = (url, data) => {
    if (!data) return '';
    // 用于标识是否包含?
    const mark = url.includes('?') ? '&' : '?';
    return `${mark}${data}`;
}
// 序列化:将传入的对象进行序列化并且进行编码防止有中文文字,如username=changlu&age=18
const serialize = (param) => {
    const results = [];
    for (const [key, value] of Object.entries(param)) {
        results.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
    }
    return results.join('&');
}
// JSON序列化(对象=>JSON字符串,通常放置在请求体中)
const serializeJSON = (data) => {
    return JSON.stringify(data);
}
export { addUrlData, serialize, serializeJSON }




②Defaults.js(默认属性方法配置类)

包含一个xhr对象的相关属性以及方法,目的是方便设置一些默认值:


属性:请求方法、请求头信息、请求体数据、内容类型、响应类型、超时时间、跨域是否携带cookie属性。

方法(监听方法):成功事件、响应码方法、报错方法、停止请求方法、超时方法。

import { HTTP_GET, CONTENT_TYPE_FROM_URLENCODED } from './constant.js'


// 默认配置类
const DEFAULTS = {
    //请求方法(多个,使用常量文件中的):默认GET请求
    method: HTTP_GET,
    // 请求头携带的数据:?username=xx&age=xx
    params: null,
    // 请求体携带的数据:①对象形式。②FormData数据
    data: null,
    // 内容类型(请求头)
    contentType: CONTENT_TYPE_FROM_URLENCODED,
    // 响应类型
    responseType: '',
    // 超时时间
    timeout: 0,
    // 跨域携带cookie属性
    withCredentials: false,
    // 监听事件方法
    success() { },
    httpCodeError() { },//响应码错误
    error() { },
    abort() { },
    timeOut() { }
};
export default DEFAULTS;




③constant.js(常量文件)

目的:将一些常量使用文件管理起来,若是想要修改一个值直接修改配置文件即可,而不是在js中来多次修改常量。


包含如HTTP请求,内容类型以及错误响应码以及描述。


// HTTP请求
export const HTTP_GET = "GET";
// 发送内容类型
export const CONTENT_TYPE_FROM_URLENCODED = 'application/x-www-form-urlencoded';
export const CONTENT_TYPE_FROM_JSON = 'application/json';
// 封装Promise使用
export const ERROR_HTTP_CODE = 1;
export const ERROR_HTTP_CODE_TEXT = 'HTTP状态码异常';
export const ERROR_REQUEST = 2;
export const ERROR_REQUEST_TEXT = '请求被阻止';
export const ERROR_ABORT = 3;
export const ERROR_ABORT_TEXT = '请求停止';
export const ERROR_TIMEOUT = 4;
export const ERROR_TIMEOUT_TEXT = '请求超时';




④Ajax.js(自定义Ajax类,用来进行初始化执行一系列操作)

其中封装了如下的步骤在构造器中:


1、构造器传入url、options初始化。


2、监听事件绑定,如success、error、errorhttpcode、abort、timeout等等,方便外部调用者直接外部写回调函数。


3、open()初始化。(请求方法与请求参数是否拼接)


4、特殊属性定制设置,如ResponseType、timeout、WithCredentials。(这部分是将options配置对象中的值真正赋给xhr对象)


5、发送请求send()。考虑多种请求①发送GET请求,其他为POST请求。②data为FormData类型。③data为x-www-form-urlencoded类型传递。④data为JSON类型传递。⑤其他类型传递。


// 导入配置类
import DEFAULTS from './default.js';
// 导入工具类
import { addUrlData, serialize, serializeJSON } from './utils.js';
// 导入常量类
import {
    HTTP_GET,
    CONTENT_TYPE_FROM_URLENCODED,
    CONTENT_TYPE_FROM_JSON
} from "./constant.js";
class Ajax {
    constructor(url, options) {
        this.url = url;
        this.options = Object.assign({}, DEFAULTS, options);
        // 初始化方法
        this.init();
    }
    // 初始化
    init() {
        const xhr = new XMLHttpRequest();
        this.xhr = xhr;
        // 绑定事件
        this.bindEvents();
        xhr.open(this.options.method, this.url + this.addParam(), true);
        // 设置属性到xhr中
        // 设置响应类型
        this.setResponseType();
        // 设置超时时间
        this.setTimeout();
        // 设置跨域是否携带cookie
        this.setWithCredentials();
        // 发送请求
        this.send();
    }
    // 绑定响应事件处理程序
    bindEvents() {
        const xhr = this.xhr;
        const { success, httpCodeError, error, abort, timeOut } = this.options;
        // 绑定监听success事件(通过监听onload事件)
        xhr.addEventListener("load", () => {
            if (this.ok()) {
                success(xhr.response, xhr);
            } else {
                httpCodeError(xhr.response, xhr);
            }
        }, false);
        // error
        xhr.addEventListener("error", () => {
            error(xhr);
        }, false);
        // abort
        xhr.addEventListener("abort", () => {
            abort(xhr);
        }, false);
        // timeout
        xhr.addEventListener("timeout", () => {
            timeOut(xhr);
        }, false);
    }
    // 判断当前状态码是否为成功
    ok() {
        const xhr = this.xhr;
        return (xhr.status >= 200 && xhr.status <= 299) || xhr.status == 304;
    }
    // 在请求地址上添加数据
    addParam() {
        const { params } = this.options;
        //无请求参数
        if (!params) return '';
        // 考虑到传入的url地址是否已经带有?,所以传入URL以及params
        return addUrlData(this.url, serialize(params));
    }
    // 设置响应类型
    setResponseType() {
        this.xhr.responseType = this.options.responseType;
    }
    // 设置超时时间
    setTimeout() {
        const { timeout } = this.options;
        if (timeout > 0) {
            this.xhr.timeout = timeout;
        }
    }
    // 设置是否跨域携带cookie
    setWithCredentials() {
        const { withCredentials } = this.options;
        if (withCredentials) {
            this.xhr.withCredentials = withCredentials;
        }
    }
    // 发送请求
    send() {
        const xhr = this.xhr;
        //测试是否需要使用send()来发送数据
        if (!this.isSendData()) {
            xhr.send(null);
            return;
        }
        // 处理data数据
        let resultData;//最终需要发送的data数据
        const { data } = this.options;
        // 是否为FormData格式数据
        if (this.isFormData(data)) {
            resultData = data;
        } else if (this.isFormURLEncodedData()) {
            // console.log("data=>", data);
            // 发送类型为application/x-www-form-urlencoded数据
            resultData = serialize(data);
            this.xhr.setRequestHeader('Content-Type', CONTENT_TYPE_FROM_URLENCODED);
        } else if (this.isJSONData()) {
            // 发送类型为application/json数据
            resultData = serializeJSON(data);
            this.xhr.setRequestHeader('Content-Type', CONTENT_TYPE_FROM_JSON);
        } else {
            this.setContentType();
            resultData = data;
        }
        xhr.send(resultData);
    }
    // 是否想要使用send()发送数据
    isSendData() {
        // 根据method以及data来确定
        const { method, data } = this.options;
        // data没有值或者请求方法为GET返回false
        if (!data) return false;
        if (method.toLowerCase() === HTTP_GET.toLowerCase())
            return false;
        return true;
    }
    // 是否data值为FormData类
    isFormData() {
        return this.options.data instanceof FormData;
    }
    // 是否发送 application/x-www-form-urlencoded 格式的数据
    isFormURLEncodedData() {
        return this.options.contentType
            .toLowerCase()
            .includes(CONTENT_TYPE_FROM_URLENCODED);
    }
    // 是否发送 application/json 格式的数据
    isJSONData() {
        return this.options.contentType
            .toLowerCase()
            .includes(CONTENT_TYPE_FROM_JSON);
    }
    // 发送其他类型的数据
    setContentType(contentType = this.options.contentType) {
        if (!contentType) return;
        this.xhr.setRequestHeader("Content-Type", contentType);
    }
    // 获取xhr对象
    getXHR() {
        return this.xhr;
    }
}
export default Ajax;



二、外部封装(提供给外界使用的API,含测试)


其实本质就是对Ajax类进行二次封装出API来提供为外界使用。


2.1、Ajax原生封装


如ajax、get、getJSON、post方法,这些名称都十分鲜明对应着不同的请求方式:


import Ajax from './ajax.js'
const ajax = (url, options) => {
    return new Ajax(url, options).getXHR();
}
const get = (url, options) => {
    return ajax(url, {
        ...options, method: 'GET'
    });
}
const getJSON = (url, options) => {
    return ajax(url, {
        ...options, method: 'GET', responseType: 'json'
    });
}
const post = (url, options) => {
    return ajax(url, {
        ...options, method: 'POST'
    });
}
export { ajax, get, getJSON, post }



测试


</div><div>    import { ajax, get, getJSON, post } from '../index(原生封装ajax).js'</div><div>    const url = 'https://www.imooc.com/api/http/search/suggest?words=js';</div><div>    const xhr = ajax(url, {</div><div>        method: 'POST',</div><div>        data: {</div><div>            username: 'changlu',</div><div>            age: 18</div><div>        },</div><div>        timeout: 10,</div><div>        responseType: 'json',</div><div>        success(response) {</div><div>            console.log(response);</div><div>        },</div><div>        error(xhr) {</div><div>            console.log("error");</div><div>        },</div><div>        abort() {</div><div>            console.log("abort");</div><div>        },</div><div>        timeOut() {</div><div>            console.log("timeout");</div><div>        }</div><div>    });</div><div>    // xhr.abort();</div><div>




2.2、Ajax进阶封装Promise


相较于原生封装有什么优势呢?


使用原生封装我们需要在传入对象中写一些回调函数如success、error、abort、timeout等等,这样的话也会造成代码量过多的情况。而使用Promise不再需要编写这些额外回调函数,交由Promise,开发者只需要关注then()与catch()方法即可:success=>then,error、abort、timeout=>catch捕获!!!

下面使用Promise进行封装,实际上只需要在Ajax原生封装上进行封装Promise即可:


import Ajax from './ajax.js'
// 引入错误码以及错误描述常量
import {
    ERROR_HTTP_CODE,
    ERROR_REQUEST,
    ERROR_TIMEOUT,
    ERROR_ABORT,
    ERROR_HTTP_CODE_TEXT,
    ERROR_REQUEST_TEXT,
    ERROR_TIMEOUT_TEXT,
    ERROR_ABORT_TEXT
} from './constant.js';
const ajax = (url, options) => {
    let xhr;
    const p = new Promise((resolve, reject) => {
        // 内部对几个方法再次进行封装
        xhr = new Ajax(url, {
            ...options,
            ...{
                //响应成功调用then()方法
                success(response) {
                    resolve(response);
                },
                //其他情况统一传递给catch()捕捉,传递type以及text属性,分为为错误码以及错误描述
                httpCodeError(status) {
                    reject({
                        type: ERROR_HTTP_CODE,
                        text: `${ERROR_HTTP_CODE_TEXT}:${status}`
                    })
                },
                error() {
                    reject({
                        type: ERROR_REQUEST,
                        text: ERROR_REQUEST_TEXT
                    })
                },
                abort() {
                    reject({
                        type: ERROR_ABORT,
                        text: ERROR_ABORT_TEXT
                    })
                },
                timeOut() {
                    reject({
                        type: ERROR_TIMEOUT,
                        text: ERROR_TIMEOUT_TEXT
                    })
                }
            }
        }).getXHR();;
    });
    //将xhr对象赋值给promise对象,为方便开发者可以调用到它自己额外做一些操作等
    p.xhr = xhr;
    p.ERROR_HTTP_CODE = ERROR_HTTP_CODE;
    p.ERROR_REQUEST = ERROR_REQUEST;
    p.ERROR_TIMEOUT = ERROR_TIMEOUT;
    p.ERROR_ABORT = ERROR_ABORT;
    return p;//返回Promise对象
}
const get = (url, options) => {
    return ajax(url, {
        ...options, method: 'GET'
    });
}
const getJSON = (url, options) => {
    return ajax(url, {
        ...options, method: 'GET', responseType: 'json'
    });
}
const post = (url, options) => {
    return ajax(url, {
        ...options, method: 'POST'
    });
}
export { ajax, get, getJSON, post }


测试


<script type="module">
    import { ajax, get, getJSON, post } from '../index(2封装ajax进阶Promise).js';
    const url = 'https://www.imooc.com/api/http/search/suggest?words=js';
    const p = getJSON(url, {
        params: {
            username: 'changlu',
            age: 18
        },
        timeout: 10
    });
    // 获取到xhr
    console.log(p.xhr);
    // 执行abort()来进行测试
    // p.xhr.abort();
    //统一这里来进行结果的处理!!!
    p.then((response) => {
        console.log(response);
    }).catch(error => {
        console.log("type=>" + error.type + ",text=>" + error.text);
    });
</script>



相关文章
|
7月前
|
前端开发
【学习笔记】Promise
【学习笔记】Promise
42 0
|
7月前
|
JSON 前端开发 JavaScript
AJAX 课程学习笔记三
AJAX 课程学习笔记三
|
7月前
|
JSON 缓存 前端开发
AJAX 课程学习笔记二
AJAX 课程学习笔记二
|
7月前
|
XML 前端开发 JavaScript
AJAX 课程学习笔记一
AJAX 课程学习笔记一
|
7月前
|
前端开发 JavaScript API
JavaScript学习笔记(一)promise与async
JavaScript学习笔记(一)promise与async
|
7月前
|
存储 前端开发
PROMISE解决AJAX中的串行和并行
PROMISE解决AJAX中的串行和并行
55 0
|
7月前
|
前端开发 Java
Promise--代码实现-- ajax 传统方式和 promise 方式和 promise 代码优化/重排 方式的对比--综合代码示例
Promise--代码实现-- ajax 传统方式和 promise 方式和 promise 代码优化/重排 方式的对比--综合代码示例
57 0
|
7月前
|
前端开发 API
用promise封装ajax
用promise封装ajax
49 0
|
前端开发 JavaScript API
Promise封装Ajax请求
Promise封装Ajax请求
58 0
|
JSON 前端开发 JavaScript
ajax和axios、fetch的区别
ajax和axios、fetch的区别