这个系列是个人写的第一个系列博客,也是个人第一次尝试阅读源码,为此特地开了个博客园账号。因为笔者是菜鸟,所以写的东西肯定非常混乱,内容分散,缺乏架构和组织,知识点不到位等等。这些都是可以预见的缺点,但自己还是坚持尝试阅读和记录源码,将所学的知识点总结记录以供自勉吧。

  这一篇源码的主要内容是,axios在不同平台的兼容性的实现以及浏览器发送请求的方式。

  这一块的代码在lib/default.js中。axios通过adapter获得node和浏览器两个平台中的不同的适配器。

function getDefaultAdapter() {
    var adapter;
    if (typeof XMLHttpRequest !== 'undefined') {
        // 浏览器使用的adapter
        adapter = require('./adapters/xhr');
    } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
        // node 使用的adapter
        adapter = require('./adapters/http');
    }
    return adapter;
}

   从代码中可以看到,axios根据浏览器和node环境独有的属性区分不同的平台。浏览器环境中实现了XMLHttpRequest对象,因此该对象存在,便返回一个浏览器平台使用的适配器。而根据require("./adapters/xhr")找打xhr.js。我们可以看到一个非常非常长的

xhrAdapter()方法。

  由于代码太长,这次就不拆解了。这篇文章和这次阅读的目的,是为了弄清浏览器和node是怎么发送http请求的。因此我只做相应的截取来记录。

  首先浏览器发送请求的第一步,就是新建一个xhr对象。

var request = new XMLHttpRequest();

       然后进行最基础的basic认证。

if (config.auth) {
      var username = config.auth.username || '';
      var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : '';
      requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
    }

  这种认证方式如此朴实无华,把用户名和密码拿冒号连接起来,再用window.btoa()方法加密成一个base-64编码的字符串就行了。

      加密完成后便是打开连接。

     

var fullPath = buildFullPath(config.baseURL, config.url);
    request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true)

           fullPath方法和buildURL方法就不细说了,主要作用是拼凑url。重要的是open方法。通常我把浏览器发送请求分为四个步骤,打开连接,发送数据,接收数据,关闭连接。XMLHttpRequest.open()便是打开连接。该方法的详细讲解请看MDN https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/open 。而XMLHttpRequest的属性readyState便用五个状态区分浏览器发送请求的四个步骤,详请如下。

axios请求配置Authorization_ios

 

 

   了解了open方法和readyState方法后我们继续往下读,可以看到一下代码 : 

 

   

request.onreadystatechange = function handleLoad() {
      if (!request || request.readyState !== 4) {
        return;
      }

      // The request errored out and we didn't get a response, this will be
      // handled by onerror instead
      // With one exception: request that using file: protocol, most browsers
      // will return status as 0 even though it's a successful request
      if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
        return;
      }

      // 获取response数据
      var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
      var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;
      var response = {
        data: responseData,
        status: request.status,
        statusText: request.statusText,
        headers: responseHeaders,
        config: config,
        request: request
      };

      settle(resolve, reject, response);

      // Clean up request
      request = null;
    };

   XMLHttpRequest.onreadyState() 方法从方法名就很明显地看出,这个方法在readyState改变时被调用。

        

if (!request || request.readyState !== 4) {
        return;
      }

  结合上面的readyState的含义不难理解,该函数是在成功接收数据后再调用。而readyState变为4,也就是请求完成,所有数据都成功获取后再开始执行后面的操作,与就是获取返回的数据。

var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
      var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;
      var response = {
        data: responseData,
        status: request.status,
        statusText: request.statusText,
        headers: responseHeaders,
        config: config,
        request: request
      };

  这一段代码中涉及的属性的XHRHttpRequest的MDN文档上都有说明,就不细说了。

       最后我们用原生的js配合XHRHttpRequest对象,根据这次阅读学到的知识,来写一个简单的请求吧。

     

// 获取xhr对象
   var xhr = new XMLHttpRequest();
   
   // 打开连接
    xhr.open("GET", "https://www.fastmock.site/mock/53689431081330c231eb05110e2161b0/first/api/test", true);

   // 监听连接状况,获取数据后将结果及状态打印出来
    xhr.onreadystatechange = function() {
        if (xhr.readyState !== 4) {
            return;
        }

        if (xhr.response) {
            console.log("responseData" + xhr.response);
        }

        if (xhr.responseText) {
            console.log("responseText" + xhr.responseText);
        }

        if (xhr.getAllResponseHeaders) {
            console.log("responseHeader" + xhr.getAllResponseHeaders())
        }

        console.log(xhr.status, xhr.statusText);

    }
     
    // 发送请求
    xhr.send();