在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);//发送异步请求 }); };