D3原始碼解讀系列之Requests

arlendp2012發表於2019-11-01

檔案的載入對於很多應用都格外重要,對d3來說也是如此。對於繪製結構簡單、資料量不大的圖形,尚且可以將資料和js程式碼存放在一起,但是對於資料結構複雜、資料量龐大的情況,資料集應該被單獨存放於獨立的檔案中,因此這裡就涉及到了檔案的讀取問題。該模組用於對原生的XMLHttpRequest進行封裝,可用於載入檔案。

d3.request

// d3.request
function request(url, callback) {
    var request,
        event = dispatch("beforesend", "progress", "load", "error"),
        mimeType,
        headers = map$1(),
        xhr = new XMLHttpRequest,
        user = null,
        password = null,
        response,
        responseType,
        timeout = 0;

    // 對於不支援跨域資源共享的IE瀏覽器,採用XDomainRequest
    if (typeof XDomainRequest !== "undefined"
        && !("withCredentials" in xhr)
        && /^(http(s)?:)?\/\//.test(url)) xhr = new XDomainRequest;
    //如果支援onload回撥方法則繫結該方法,否則使用onreadystatechange方法
    "onload" in xhr
        //三種事件分別是請求成功完成、請求失敗和請求時限到期未完成
        ? xhr.onload = xhr.onerror = xhr.ontimeout = respond
        : xhr.onreadystatechange = function(o) { xhr.readyState > 3 && respond(o); };

    function respond(o) {
      var status = xhr.status, result;
      if (!status && hasResponse(xhr)
          || status >= 200 && status < 300
          || status === 304) {
        if (response) {
          try {
            //先通過response函式對返回結果進行處理
            result = response.call(request, xhr);
          } catch (e) {
            event.call("error", request, e);
            return;
          }
        } else {
          result = xhr;
        }
        event.call("load", request, result);
      } else {
        event.call("error", request, o);
      }
    }

    xhr.onprogress = function(e) {
      event.call("progress", request, e);
    };

    request = {
      //設定請求頭資訊
      header: function(name, value) {
        name = (name + "").toLowerCase();
        if (arguments.length < 2) return headers.get(name);
        if (value == null) headers.remove(name);
        else headers.set(name, value + "");
        return request;
      },

      // 設定伺服器返回資料型別,用於accept請求頭和overrideMimeType方法
      mimeType: function(value) {
        if (!arguments.length) return mimeType;
        mimeType = value == null ? null : value + "";
        return request;
      },

      /* 指定返回值型別,可以為以下幾種值:
       * ”“:字串(預設值)
       * “arraybuffer”:ArrayBuffer物件
       * “blob”:Blob物件
       * “document”:Document物件
       * “json”:JSON物件
       * “text”:字串
       */
      responseType: function(value) {
        if (!arguments.length) return responseType;
        responseType = value;
        return request;
      },

      timeout: function(value) {
        if (!arguments.length) return timeout;
        timeout = +value;
        return request;
      },

      user: function(value) {
        return arguments.length < 1 ? user : (user = value == null ? null : value + "", request);
      },

      password: function(value) {
        return arguments.length < 1 ? password : (password = value == null ? null : value + "", request);
      },

      //將返回內容轉化成指定型別
      response: function(value) {
        response = value;
        return request;
      },

      // 使用GET方法傳送請求
      get: function(data, callback) {
        return request.send("GET", data, callback);
      },

      // 使用POST方法傳送請求
      post: function(data, callback) {
        return request.send("POST", data, callback);
      },

      // 修改請求頭資訊,設定伺服器返回資料型別,監聽error和load事件並設定回撥函式
      send: function(method, data, callback) {
        xhr.open(method, url, true, user, password);
        if (mimeType != null && !headers.has("accept")) headers.set("accept", mimeType + ",*/*");
        //設定請求頭
        if (xhr.setRequestHeader) headers.each(function(value, name) { xhr.setRequestHeader(name, value); });
        //指定伺服器返回資料型別
        if (mimeType != null && xhr.overrideMimeType) xhr.overrideMimeType(mimeType);
        if (responseType != null) xhr.responseType = responseType;
        if (timeout > 0) xhr.timeout = timeout;
        if (callback == null && typeof data === "function") callback = data, data = null;
        if (callback != null && callback.length === 1) callback = fixCallback(callback);
        if (callback != null) request.on("error", callback).on("load", function(xhr) { callback(null, xhr); });
        //呼叫beforesend監聽事件
        event.call("beforesend", request, xhr);
        //傳送請求
        xhr.send(data == null ? null : data);
        return request;
      },

      abort: function() {
        xhr.abort();
        return request;
      },
      //設定事件監聽函式,只能是以下型別:beforesend、progress、load和error
      on: function() {
        var value = event.on.apply(event, arguments);
        return value === event ? request : value;
      }
    };
    //如果callback引數被傳入,則會立即將請求傳送出去,若沒有傳入callback則可繼續配置request
    if (callback != null) {
      if (typeof callback !== "function") throw new Error("invalid callback: " + callback);
      return request.get(callback);
    }

    return request;
}
複製程式碼

d3.csv

用於讀取指定URL中的csv檔案。

//d3.csv
var csv$1 = dsv$1("text/csv", csvParse);

function dsv$1(defaultMimeType, parse) {
    return function(url, row, callback) {
      //可以省略row函式
      if (arguments.length < 3) callback = row, row = null;
      var r = request(url).mimeType(defaultMimeType);
      //設定row函式
      r.row = function(_) { return arguments.length ? r.response(responseOf(parse, row = _)) : row; };
      r.row(row);
      return callback ? r.get(callback) : r;
    };
}
  
//返回解析函式
function responseOf(parse, row) {
    return function(request) {
      return parse(request.responseText, row);
    };
}
  
複製程式碼

這部分是直接呼叫request方法來讀取檔案,修改了伺服器返回資料型別和response方法。與以下方法等同:

d3.request(url)
    .mimeType("text/csv")
    .response(function(xhr) { return d3.csvParse(xhr.responseText, row); })
    .get(callback);
複製程式碼

d3.html

用於讀取HTML檔案。

var html = type("text/html", function(xhr) {
    return document.createRange().createContextualFragment(xhr.responseText);
});
複製程式碼

將返回的字串構造成document fragment,形成一個DOM節點可以對其進行操作。

d3.json

讀取JSON檔案。

//d3.json
var json = type("application/json", function(xhr) {
    return JSON.parse(xhr.responseText);
});
複製程式碼

通過JSON.parse方法對返回的字串進行處理,轉化成json格式的資料。

d3.tsv

用於讀取tsv檔案,和上述d3.csv類似。 等同於:

d3.request(url)
    .mimeType("text/tab-separated-values")
    .response(function(xhr) { return d3.tsvParse(xhr.responseText, row); })
    .get(callback);
複製程式碼

相關文章