【Ajax】一点疑惑,关于 jQuery 的 ajax 接口

一点疑惑

在接触 jQuery 的 ajax api 之前我一直在封装自己的接口
代码:

// Ajax 封装
function Ajax() {
    var makeCallback = function (xhr, callback) {
        xhr.onerror = function (err) {
            callback(err, undefined, xhr);
        };
        xhr.onload = function () {
            var contentType = xhr.getResponseHeader('Content-Type');
            callback(undefined, contentType && contentType.indexOf('application/json') != -1
                ? JSON.parse(xhr.responseText)
                : xhr.responseText, xhr);
        };
    };
    var toQueryString = function () {
        function recursive(value, key) {
            var queryStr = '';
            if (typeof value != 'object')
                queryStr += key + '=' + value + '&';
            else
                for (var item in value)
                    queryStr += key == undefined ? recursive(value[item], item) : recursive(value[item], key + '[\'' + item + '\']');
            return queryStr;
        }
        return function (value) {
            return recursive(value).slice(0, -1);
        }
    }();
    this.get = function (options) {
        options.data = options.data || '';
        var xhr = new XMLHttpRequest();
        options.pre(xhr) && options.pre(xhr);
        makeCallback(xhr, options.callback);
        xhr.open('get', options.url + '?' + (typeof options.data == 'string' ? options.data : toQueryString(options.data)));
        xhr.send();
    };
    this.postUrl = function postUrl(options) {
        options.data = options.data || '';
        var xhr = new XMLHttpRequest();
        options.pre(xhr) && options.pre(xhr);
        makeCallback(xhr, options.callback);
        xhr.open('post', options.url);
        if (options.data instanceof FormData) { return xhr.send(options.data) };
        xhr.setRequestHeader(
            'Content-Type',
            'application/x-www-form-urlencoded'
        );
        this.xhr.send(
            typeof options.data == 'string' ? options.data : toQueryString(options.data)
        );
    }
    this.postJson = function (options) {
        options.data = options.data || '';
        var xhr = new XMLHttpRequest();
        options.pre(xhr) && options.pre(xhr);
        makeCallback(xhr, options.callback);
        xhr.open('post', options.url);
        if (options.data instanceof FormData) { return xhr.send(options.data) };
        xhr.setRequestHeader(
            'Content-Type',
            'application/json'
        );
        xhr.send(
            typeof options.data == 'string' ? options.data : JSON.stringify(options.data)
        );
    }
    this.post = this.postJson;
}

// ES6 版本
class Ajax {
    #toQueryString = function () {
        function recursive(value, key) {
            var queryStr = '';
            if (typeof value != 'object')
                queryStr += key + '=' + value + '&';
            else
                for (let item in value)
                    queryStr += key == undefined ? recursive(value[item], item) : recursive(value[item], key + '[\'' + item + '\']');
            return queryStr;
        }
        return function (value) {
            return recursive(value).slice(0, -1);
        }
    }();
    #makeCallback = function (xhr, callback) {
        xhr.onerror = function (err) {
            callback(err, undefined, xhr);
        };
        xhr.onload = function () {
            let contentType = xhr.getResponseHeader("Content-Type");
            callback(
                undefined,
                contentType && contentType.includes("application/json")
                    ? JSON.parse(xhr.responseText)
                    : xhr.responseText,
                xhr
            );
        };
    };

    get({ url, data, callback, pre }) {
        data = data || '';
        const xhr = new XMLHttpRequest();
        pre && pre(xhr);
        this.#makeCallback(xhr, callback);
        xhr.open(
            "get",
            url + "?" + (typeof data == "string" ? data : this.#toQueryString(data))
        );
        xhr.send();
    }
    postUrl({ url, data, callback, pre }) {
        data = data || '';
        const xhr = new XMLHttpRequest();
        pre && pre(xhr);
        this.#makeCallback(xhr, callback);
        xhr.open("post", url);
        if (data instanceof FormData) { return xhr.send(data) };
        xhr.setRequestHeader(
            "Content-Type",
            "application/x-www-form-urlencoded"
        );
        xhr.send(typeof data == "string" ? data : this.#toQueryString(data));
    }
    postJson({ url, data, callback, pre }) {
        data = data || '';
        const xhr = new XMLHttpRequest();
        pre && pre(xhr);
        this.#makeCallback(xhr, callback);
        xhr.open("post", url);
        if (data instanceof FormData) { return xhr.send(data) };
        xhr.setRequestHeader("Content-Type", "application/json");
        xhr.send(typeof data == "string" ? data : JSON.stringify(data));
    }
    post({ url, data, callback, pre }) {
        this.postJson({ url, data, callback, pre });
    }
}

可以发现,在调用我的接口时,data 字段传入的均为对象。
实际上对于调用者来说,允许直接传对象是一种非常友好的接口设计。

接下来上 jQuery 的接口:

// url
$.ajax(
    {
        type: 'get',

        url: 'http://example.com/route',

        data: { content: 'content' },

        contentType: 'application/x-www-form-urlencoded',// 可缺省

        beforeSend: function () {
            return false;
        },

        success: function (res) {
            res;
        },

        error: function (xhr) {

        }
    }
);
// json
$.ajax(
    {
        type: 'post',

        url: 'http://example.com/route',

        data: JSON.stringify({ content: 'content' }),

        contentType: 'application/json',

        beforeSend: function () {
            return false;
        },

        success: function (res) {
            res;
        },

        error: function (xhr) {

        }
    }
);

当我们需要 post 类型为 application/json 即 json 格式数据时,我们必须在将对象传入 data 字段前调用
JSON.stringify()
包裹一下。

这就很不符合常理

首先第一点,这是我最不可理解的地方,
我们知道,web apis 仅提供了对象到 json 字符串的 stringify 方法,而没有提供对象到 url querystring 的接口。
那么对于 jQuery ajax 接口的默认提交方式 urlencoded 来说,其还需要额外封装一个对象到 querystring 的方法,然后在内部为我们将对象转换为 querystring。
而其对于提供了接口的 json 格式却不予转换。

有些人可能觉得对象 to querystring是一个非常简单的的事情,不过是一个循环而已,然而并不是。
json 是一个树结构,换言之,我们需要遍历树,并且为每一个节点用 obj[node].obj1[node1] 这样风格的字符串表示
由于我的算法基础比较菜,我那天写到凌晨两点,最后才用递归实现了

function toQueryString () {
        function recursive(value, key) {
            var queryStr = '';
            if (typeof value != 'object')
                queryStr += key + '=' + value + '&';
            else
                for (let item in value)
                    queryStr += key == undefined ? recursive(value[item], item) : recursive(value[item], key + '[\'' + item + '\']');
            return queryStr;
        }
        return function (value) {
            return recursive(value).slice(0, -1);
        }
    }();

不过具体实现并不是重点,重点是,
为什么 jQuery 费老大劲为我们封装了 toQueryString,却不肯为我们的 json 调用一下 JSON.stringify() ?

然后是第二点 ,
jQuery 接口内部完全可以通过判断 contentType 字段的参数来为 data 字段进行不同的处理,这并不是很麻烦的事情,甚至可以说是顺手解决的事,他为什么不这么做?

posted @ 2020-04-13 00:33  高厉害  阅读(147)  评论(0编辑  收藏  举报