在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);//發送異步請求 }); };
