Blog

Blog

PHODAL

Chrome 插件开发与XMLHttpRequest漫游

试了试Chrome下的插件开发,简单将一些有意思的东西列了出来,其中一个就是XMLHttpRequest。

XMLHttpRequest

关于XMLHttp维基上有下面的介绍

XMLHTTP是一组API函数集,可被JavaScript、JScript、VBScript以及其它web浏览器内嵌的脚本语言调用,通过HTTP在浏览器和web服务器之间收发XML或其它数据。XMLHTTP最大的好处在于可以动态地更新网页,它无需重新从服务器读取整个网页,也不需要安装额外的插件。该技术被许多网站使用,以实现快速响应的动态网页应用。例如:Google的Gmail服务、Google Suggest动态查找界面以及Google Map地理信息服务。

XMLHTTP是AJAX网页开发技术的重要组成部分。

除XML之外,XMLHTTP还能用于获取其它格式的数据,如JSON或者甚至纯文本。

在Chrome的开发文档中有:

普通网页能够使用XMLHttpRequest对象发送或者接受服务器数据, 但是它们受限于同源策略. 扩展可以不受该限制. 任何扩展只要它先获取了跨域请求许可,就可以进行跨域请求。

之前没有试过用原生的XMLHTTP写过代码,也算是一种不象牙的体验。

XMLHTTPRequest示例

于是看到官方的文档是这样子的

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = handleStateChange;
xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true);
xhr.send();

最后只需要return个

xhr.responseText

就可以了。

jQuery XMLHTTPRequest

于是看了看jQuery的ajax首先是这样的一个函数,提供XHR

jQuery.ajaxSettings.xhr = function() {
try {
    return new XMLHttpRequest();
} catch( e ) {}
};

然后有

xhrSupported = jQuery.ajaxSettings.xhr();
support.ajax = xhrSupported = !!xhrSupported;

越往后面越复杂就不继续了,看看Amazon是怎么封装的。

Amazon XMLHTTPRequest

先看看一个简单的对Amazon的XHRClient的封装

XHRHelper.prototype.request = function(url, callback) {
    var xhrClient = new XHRClient();
    var options = {};

    options["success"] = function(response) {
        callback(response);
    };
    xhrClient.request(url, options);
}

用的时候就是这么用的

        var xhrHelper = new XHRHelper();
        xhrHelper.request(url, function(result) {
             console.log(result);
        });

但是封装之前的代码呢?

Amazon XHRClient流程

首先会对传进来的参数做一个merge,这也就是我们只传进一个options["success"]的原因。

    options = options || {};
    _.defaults(options, {
        http_method: "GET",
        responseType: "json",
        headers: {},
        success: function() {},
        error: function() {},
        data: {},
        timeout: 0,
        cacheDateHeader: false
    });

不得不说这确实是一个好的方法——用来处理一些默认的选项。

接着对options进行了一些处理

    if (!url || typeof url !== "string") {
        options['error'](new Error("URL is an invalid type"));
    }
    if (options['http_method'] === HTTP_METHOD['GET']) {
        url += hasParams(url) ? "&" : "?";
        url += serialize(options['data']);
    }

    request.open(options['http_method'], url, true);
    _.each(_.keys(options['headers']), function(key) {
        request.setRequestHeader(key, options['headers'][key]);
    });

    request.responseType = options['responseType'];

    request.timeout = options['timeout'];
    request.ontimeout = function() {
        options['error'](new Error("The request timed out"));
    };

然后是获取数据,如果可以获取到数据的时候,解析数据。

    request.onreadystatechange = _.bind(function() {
        if (request.readyState === STATE.DONE) {
            if (request.status === STATUS.OK) {
                var response, error = new Error("Unable to parse the response");
                try {
                    response = parseResponse(request);
                }
                catch(e) {
                    response = null;
                }

                if( response ) {
                    options['success'](response);
                } else {
                    options['error'](error);
                }
            } else {
                var error = new Error("Request completed with a non-200 status code");
                error.status = request.status;
                error.statusText = request.statusText;

                options['error'](error);
            }
        } else if (request.readyState === STATE.HEADERS_RECEIVED) {
            var httpResponseHeaderDate = request.getResponseHeader("Date");

            if (httpResponseHeaderDate && options.cacheDateHeader) {
                try {
                    var serverClientTimePairObject = {
                        "serverTime" : Date.parse(httpResponseHeaderDate),
                        "clientTime" : Date.now()
                    };
                } catch (error) {}
            }
        }
    },this);

Amazon Ajax封装

下面的代码是来自Amazon的,除去之前的模块化——亚马逊1Button App源码分析之JavaScript模块化思想,我们关注一下这个实现:

var Ajax = function() {};

Ajax.prototype = {
    get: function(url, cb) {
        cb = cb || lang.noop;
        if (typeof XMLHttpRequest !== "undefined") {
            var xhr = new XMLHttpRequest();
            xhr.open("GET", url, true);
            xhr.onreadystatechange = function() {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200) {
                        cb(null, xhr.responseText);
                    } else {
                        var err = new Error("Server returned with non-200 status code: " + xhr.status);
                        err.xhr = xhr;
                        cb(err);
                    }
                }
            }
            xhr.send();
        }
    }
};

实现上只是刚之前的xhr-client.js重新实现了一遍,然后在上面写着

/**
 * @exports a singleton instance of Ajax
 */

zepto(ps:jQuery兼容库)中是这样实现的:

$.ajax = function(options){
    xhr.setRequestHeader = setHeader

    xhr.onreadystatechange = function(){
      if (xhr.readyState == 4) {
        xhr.onreadystatechange = empty
        clearTimeout(abortTimeout)
        var result, error = false
        if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) {
          dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type'))
          result = xhr.responseText

          try {
            // http://perfectionkills.com/global-eval-what-are-the-options/
            if (dataType == 'script')    (1,eval)(result)
            else if (dataType == 'xml')  result = xhr.responseXML
            else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result)
          } catch (e) { error = e }

          if (error) ajaxError(error, 'parsererror', xhr, settings, deferred)
          else ajaxSuccess(result, xhr, settings, deferred)
        } else {
          ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred)
        }
      }
    }
    return xhr
}

或许您还需要下面的文章:

关于我

Github: @phodal     微博:@phodal     知乎:@phodal    

微信公众号(Phodal)

围观我的Github Idea墙, 也许,你会遇到心仪的项目

QQ技术交流群: 321689806
comment

Feeds

RSS / Atom

最近文章

关于作者

Phodal Huang

Engineer, Consultant, Writer, Designer

ThoughtWorks 技术专家

工程师 / 咨询师 / 作家 / 设计学徒

开源深度爱好者

出版有《前端架构:从入门到微前端》、《自己动手设计物联网》、《全栈应用开发:精益实践》

联系我: h@phodal.com

微信公众号: 最新技术分享

标签