瀏覽器環境xhr請求


在axios的defaults.js文件里有getDefaultAdapter這個方法,用來判斷環境,然后返回對應的請求適配器。

function getDefaultAdapter() {//獲取默認適配器
  var adapter;
  // Only Node.JS has a process variable that is of [[Class]] process
  //只有nodejs環境下才有process對象,是Process類的實例
  if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    // For node use HTTP adapter
    adapter = require('./adapters/http');//nodejs環境的請求工具
  } else if (typeof XMLHttpRequest !== 'undefined') {
    // For browsers use XHR adapter
    adapter = require('./adapters/xhr');//瀏覽器環境的請求工具
  }
  return adapter;
}

XMLHttpRequest

瀏覽器環境使用XMLHttpRequest對象發送請求

步驟如下

下面是xhr.js源代碼:

'use strict';

var utils = require('./../utils');
var settle = require('./../core/settle');
var buildURL = require('./../helpers/buildURL');
var parseHeaders = require('./../helpers/parseHeaders');
var isURLSameOrigin = require('./../helpers/isURLSameOrigin');
var createError = require('../core/createError');

module.exports = function xhrAdapter(config) {
  return new Promise(function dispatchXhrRequest(resolve, reject) {//返回一個promise
    var requestData = config.data;//請求的data參數
    var requestHeaders = config.headers;//請求頭

    if (utils.isFormData(requestData)) {
      //判斷data是否是FormData對象,如果是,就刪除Content-Type請求頭,讓瀏覽器自己設置Content-Type
      delete requestHeaders['Content-Type']; // Let the browser set it
    }

    var request = new XMLHttpRequest();//新實例化XML請求對象

    // HTTP basic authentication
    if (config.auth) {//配置里的身份驗證參數,會覆蓋掉headers里的Authorization
      var username = config.auth.username || '';
      var password = config.auth.password || '';
      requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
      //用window.btoa將用戶名和密碼進行base64轉碼
    }

    request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true);
    //XMLHttpRequest.open(method, url, async)
    //初始化一個請求,method請求的類型,url請求的資源地址,async是否執行異步操作

    // Set the request timeout in MS
    request.timeout = config.timeout;//設置超時時間到XMLHttpRequest對象上

    // Listen for ready state
    request.onreadystatechange = function handleLoad() {
      //XMLHttpRequest.onreadystatechange 會在 XMLHttpRequest 的readyState 屬性發生改變時觸發 readystatechange 事件的時候被調用
      if (!request || request.readyState !== 4) {
        //XMLHttpRequest.readyState 當前所處的狀態
        //0剛被創建,1open初始化完成,2已經send,3下載中,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
      //如果請求報錯沒有獲取到響應,就會觸發onerror處理
      //有一個例外,請求使用了文件協議,大多數瀏覽器會返回狀態0即使請求成功了
      if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {//如果響應的狀態碼為0,並且響應的最終url是file協議就return
        return;
      }

      // Prepare the response
      var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
      //用XMLHttpRequest.getAllResponseHeaders()獲取所有響應頭,並將其轉換成一個對象
      var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;//獲取響應正文,根據responseType的類型來獲取
      var response = {//生成自定義響應正文數據
        data: responseData,//響應正文
        status: request.status,//請求數字狀態碼
        statusText: request.statusText,//數字狀態碼對應的文本信息
        headers: responseHeaders,//響應頭
        config: config,//請求配置
        request: request//XMLHttpRequest對象
      };

      settle(resolve, reject, response);//根據狀態碼resolve或者reject這個promise

      // Clean up request
      request = null;//清除請求對象
    };

    // Handle browser request cancellation (as opposed to a manual cancellation)
    request.onabort = function handleAbort() {
      //當一個資源的加載已中止時,將觸發 abort事件
      if (!request) {//如果請求對象為空,返回
        return;
      }

      reject(createError('Request aborted', config, 'ECONNABORTED', request));
      //reject掉promise

      // Clean up request
      request = null;//清除請求對象
    };

    // Handle low level network errors
    request.onerror = function handleError() {
      //資源加載失敗時觸發error事件,原因是網絡錯誤
      // Real errors are hidden from us by the browser
      // onerror should only fire if it's a network error
      reject(createError('Network Error', config, null, request));
      //reject掉promise
      // Clean up request
      request = null;//清除請求對象
    };

    // Handle timeout
    request.ontimeout = function handleTimeout() {
      //超時的時候觸發timeout事件
      reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED',
        request));
      //reject掉promise
      // Clean up request
      request = null;//清除請求對象
    };

    // Add xsrf header
    // This is only done if running in a standard browser environment.
    // Specifically not if we're in a web worker, or react-native.
    //添加xsrf請求頭,這個請求頭只有在標准瀏覽器環境下運行的情況會添加
    if (utils.isStandardBrowserEnv()) {//isStandardBrowserEnv判斷是否在標准瀏覽器環境下
      var cookies = require('./../helpers/cookies');

      // Add xsrf header
      //添加xsrf 自定義請求頭
      var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ?
        cookies.read(config.xsrfCookieName) :
        undefined;
        //如果withCredentials為true或者請求url與頁面同域並且傳遞了xsrfCookieName參數
        //就從cookie中讀取xsrfCookieName對應cookie值

      if (xsrfValue) {//如果對應cookie值存在,就像其加入到headers中
        requestHeaders[config.xsrfHeaderName] = xsrfValue;
      }
    }

    // Add headers to the request
    //添加請求頭到請求對象中
    if ('setRequestHeader' in request) {
      utils.forEach(requestHeaders, function setRequestHeader(val, key) {//循環headers
        if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
          //如果data為空,移除Content-Type請求頭
          // Remove Content-Type if data is undefined
          delete requestHeaders[key];
        } else {//否則添加當前頭到請求對象中
          // Otherwise add header to the request
          request.setRequestHeader(key, val);
        }
      });
    }

    // Add withCredentials to request if needed
    if (config.withCredentials) {//添加withCredentials到請求對象中
      request.withCredentials = true;
    }

    // Add responseType to request if needed
    if (config.responseType) {//添加responseType到請求對象中
      try {
        request.responseType = config.responseType;//直接設置到請求對象上
      } catch (e) {
        // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.
        // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.
        //不兼容XMLHttpRequest Level 2 瀏覽器會拋出錯誤,但是如果是json類型那么可以被默認transformResponse解析,否則就拋出錯誤
        if (config.responseType !== 'json') {
          throw e;
        }
      }
    }

    // Handle progress if needed
    if (typeof config.onDownloadProgress === 'function') {
      request.addEventListener('progress', config.onDownloadProgress);
    }

    // Not all browsers support upload events
    if (typeof config.onUploadProgress === 'function' && request.upload) {
      //XMLHttpRequest.upload方法返回一個 XMLHttpRequestUpload對象,用來表示上傳的進度
      //為XMLHttpRequestUpload對象綁定事件監聽其上傳進度
      request.upload.addEventListener('progress', config.onUploadProgress);
    }

    if (config.cancelToken) {//如果配置項里面有cancelToken設置
      // Handle cancellation
      //設置了cancelToken的請求,只要用戶調用了cancelToken對象的cancel方法,就會執行下面的then回調,中止請求
      config.cancelToken.promise.then(function onCanceled(cancel) {
        if (!request) {//如果請求對象為空,不作操作直接返回
          return;
        }

        request.abort();//終止請求
        reject(cancel);//調用reject改變promise狀態
        // Clean up request
        request = null;//請求對象銷毀
      });
    }

    if (requestData === undefined) {
      requestData = null;
    }

    // Send the request
    request.send(requestData);//發送異步請求
  });
};

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM