第 40 题:如何实现一个 ajax?

主要使用以下文件

config.js:ajax 请求配置核心文件

loading.js:element-ui 请求加载动画

index.js:二次封装 config.js 请求并导出该方法,配置请求拦截器、响应拦截器

index.vue:调用 ajax 的示例页面

注意:推荐在 api 目录统一管理所有接口,如果遇到报错请调整正确引用路径

config.js

import loading from './loading.js'; // 加载动画类

const animation = false; // 接口加载动画
const intTimer = 10; // 接口请求超时时间(秒)

class Config {
    constructor(data) {
        this.method = data.method;
        this.url = data.url;
        this.param = data.param || {};
        this.header = data.header || {};
        this.interceptors = data.interceptors;
        this.response = data.response;

        return this.filter();
    }

    // 创建XHR对象
    createXHR() {
        if (window.XMLHttpRequest) {
            // code for IE7+, Firefox, Chrome, Opera, Safari
            return new XMLHttpRequest();
        } else {
            // code for IE6, IE5
            return new ActiveXObject('Microsoft.XMLHTTP');
        }
    }

    // HTTP请求
    xhrRequest(header, method, url, param, async, interceptors, response) {
        return new Promise(resolve => {
            var xhr = this.createXHR();
            if (animation == true) {
                loading.requestStart(); // 执行动画
            }

            // 请求拦截
            if (interceptors({ header, method, url: this.url, param: this.param, async })) {
                xhr.open(method, url, async);
                xhr.timeout = 1000 * intTimer; //设置xhr请求的超时时间
                Object.keys(header).map(key => {
                    xhr.setRequestHeader(key, header[key]);
                });
                xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; application/json; charset=utf-8');
                xhr.send(param);
                xhr.onreadystatechange = () => {
                    if (xhr.readyState == 4 && xhr.status == 200) {
                        loading.requestEnd(); // 结束动画
                        try {
                            let data = JSON.parse(xhr.responseText);

                            resolve(response(data, { header, method, url: this.url, param: this.param, async }));
                        } catch (error) {
                            console.log('接口返回没有任何信息!');
                            resolve(false);
                        }
                    } else {
                        return 'request is unsucessful ' + xhr.status;
                    }
                };
            } else {
                console.error('request interceptor', '请求未发出, 请求拦截器已生效!');
            }

            // 请求超时方法
            xhr.ontimeout = function(e) {
                console.log('接口请求超时!');
                loading.requestEnd(); // 结束动画
            };

            // 请求错误方法
            xhr.onerror = function(e) {
                console.log('接口请求失败');
                loading.requestEnd(); // 结束动画
            };
        });
    }

    // 参数转换
    convParams(param) {
        let mark = '?';
        let hasMark = this.url.indexOf(mark) > 0; // 是否包含特殊字符
        if (hasMark) {
            mark = '&';
        }

        let newParams = '';
        let i = 0;
        for (let key in param) {
            if (i > 0) {
                newParams += `&${key}=${param[key]}`;
            } else {
                newParams += `${mark}${key}=${param[key]}`;
            }
            i++;
        }
        return newParams;
    }

    // 数据GET、POST请求处理
    filter() {
        let obj = {
            header: this.header,
            method: this.method,
            url: this.url,
            param: {},
            async: true,
            interceptors: this.interceptors,
            response: this.response
        };

        // 接口名称拼接位置:(1、url) (2、param)

        let newParams = this.convParams(this.param);
        if (this.method == 'GET') {
            obj.url += newParams;
        } else {
            newParams = newParams.replace('?', '');
            obj.param = newParams;
        }

        return this.xhrRequest(obj.header, obj.method, obj.url, obj.param, obj.async, obj.interceptors, obj.response);
    }
}

export default Config;

loading.js

import { Loading } from 'element-ui';

class animation {
    constructor() {
        this.needLoadingRequestCount = 0;
        this.loading
    }

    /**
     * 动画开始
     */
    requestStart() {
        if (this.needLoadingRequestCount === 0) {
            this.loading = Loading.service({
                lock: true,
                text: 'loading...',
                background: 'rgba(0, 0, 0, 0.7)'
            });
        }
        this.needLoadingRequestCount++;
    }

    /**
     * 动画结束
     */
    requestEnd() {
        if (this.needLoadingRequestCount <= 0) return;
        this.needLoadingRequestCount--;
        if (this.needLoadingRequestCount === 0) {
            this.loading.close();
        }
    }
}


export default new animation()

index.js

import Config from './config.js';

/**
 * 接口请求方法
 * @func request
 * @param {Object} method 请求方式: 仅支持GET、POST
 * @param {String} url 请求地址
 * @param {Object} param 请求参数
 */
let request = option => {
    // 配置默认请求参数

    return new Config({
        header: {
            Authorization: 'APPCODE edc39cc1dc5f4c139498322115b99e51'
        },
        method: option.method,
        url: option.url,
        param: option.param,
        interceptors: interceptors,
        response: response
    });
};

/**
 * 请求拦截器
 * @func interceptors
 */
let interceptors = config => {
    return true;
};

/**
 * 响应拦截器
 * @func response
 */
let response = (data, config) => {
    let res;

    // 处理返回格式
    if (data.res) {
        res = data.res;
    } else if (data.data) {
        res = data.data;
    } else {
        res = data;
    }
    return res;
};

export default request;

index.vue

文章的内容/灵感都从下方内容中借鉴

你可能感兴趣的