The latest version of axios is v0.19.0. In this section, we will analyze its implementation source code. First, we get its source code through gitHub address, address: https://github.com/axios/axios/tree/v0.19.0
After downloading, you can see the directory structure of axios. There is an index.js file in the home directory. The file is relatively simple. The contents are as follows:
This is to introduce the. / lib/axios module. The contents of the Lib directory are as follows:
The general document is as follows:
index.js; entry file
_lib; code home directory
_helpers; Some auxiliary functions are defined
_adapters; encapsulation of requests in native ajax and node environments
_cancel; Unencapsulate some of the requests
_core; request dispatch, interceptor management, data conversion, etc.
axios.js; it's also an entry document.
default.js; default configuration file
Utls.js; Tool functions
Writby: Desert QQ:22969969
./lib/axios should also be an entry document. The main branches are as follows:
var utils = require('./utils'); var bind = require('./helpers/bind'); var Axios = require('./core/Axios'); var mergeConfig = require('./core/mergeConfig'); var defaults = require('./defaults'); //Default configuration object /*slightly*/ function createInstance(defaultConfig) { //Create a Axios The example parameter is:Axios Default configuration var context = new Axios(defaultConfig); //Create A. / lib/core/Axios object as context var instance = bind(Axios.prototype.request, context); //Create an instance attribute with a value of the return value of the bind() function // Copy axios.prototype to instance utils.extend(instance, Axios.prototype, context); //take Axios.prototype Upper method(delete,get,head,options,post,put,patch,request)extend reach instans Pass on bind Binding // Copy context to instance utils.extend(instance, context); //take context Two of them defaults and interceptors Attributes are saved to utils Above, both are objects, so that we can pass through axios.defaults Modify configuration information by axios.interceptors To set up the interceptor return instance; //Return instance method } // Create the default instance to be exported var axios = createInstance(defaults); //Create a default instance as output /*slightly*/ module.exports = axios; //Exported symbols // Allow use of default import syntax in TypeScript module.exports.default = axios; //Default export symbol
createInstance creates an object instance of. / lib/core/Axios, saves it in the context of the local variable, then calls the bind function to save the return value in the instance (which is the symbol we call when we call axios() to execute the ajax request). Bid () is an auxiliary function, as follows:
module.exports = function bind(fn, thisArg) { //with thisArg For context, execute fn function return function wrap() { var args = new Array(arguments.length); //take arguments Save in sequence to args inside for (var i = 0; i < args.length; i++) { args[i] = arguments[i]; } return fn.apply(thisArg, args); //implement fn Function, parameter is thisArg For context, args As parameter }; };
This function is a realization of a higher order function. It executes parameter 1 with parameter 2 as the context, that is, executing Axios.prototype.request function under the context of context, and Axios.prototype.request is the entry of all asynchronous requests.
Let's look at the implementation of Axios.prototype.request, as follows:
Axios.prototype.request = function request(config) { //Send a request,Also ajax Request entry /*eslint no-param-reassign:0*/ // Allow for axios('example/url'[, config]) a la fetch API if (typeof config === 'string') { //If config Object is a string. ;for example:axios('/api/1.php').then(function(){},function(){}) config = arguments[1] || {}; //Convert it to an object config.url = arguments[0]; } else { config = config || {}; } config = mergeConfig(this.defaults, config); //Merge defaults config.method = config.method ? config.method.toLowerCase() : 'get'; //ajax Method, for example:get,Here is the conversion to lowercase // Hook up interceptors middleware var chain = [dispatchRequest, undefined]; //This is sending ajax Asynchronous alignment of var promise = Promise.resolve(config); //take config Convert to Promise object this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { //Logic of Request Interceptor(Introduction to the next section) chain.unshift(interceptor.fulfilled, interceptor.rejected); }); this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { //Logic of Response Interception(Next section) chain.push(interceptor.fulfilled, interceptor.rejected); }); while (chain.length) { //If chain.length existence promise = promise.then(chain.shift(), chain.shift()); //Then execute promise.then(),Execute here dispatchRequest Function, which makes up an asynchronous queue } return promise; //Last return promise object };
Here's a while(chain.length) {} traversal loop that's hard to understand. This design idea is novel. If you understand the whole axios execution process, you can understand it. The interceptor is also implemented here. It traverses the chain array and executes the first two elements as promise (). the first two elements in turn as parameters 1 and 2 of promise().then the queue before promise is executed and then the queue after promise is executed. The default is [dispatchRequest,undefined], that is, dispatchRequest is executed first, and interception is executed before dispatchRequest if a request interceptor is added. Similarly, if there is a response interceptor, the logic in the response interceptor will be executed after dispatchRequest.
The dispatchRequest logic is as follows:
module.exports = function dispatchRequest(config) { //To dispatch a request to the server, use config Configuration inside throwIfCancellationRequested(config); // Support baseURL config if (config.baseURL && !isAbsoluteURL(config.url)) { //If config.baseURL Existence, and config.url Not absolute URL(with http://Beginning) config.url = combineURLs(config.baseURL, config.url); //Then call combineURLs take config.baseURL Pieced together config.url In the front, we set it up in the project. baseURL="api/"It's here that we deal with it. } // Ensure headers exist config.headers = config.headers || {}; //ensure headers existence // Transform request data config.data = transformData( //Modifying the request data will call the default configuration transformRequest Processing config.data, config.headers, config.transformRequest ); // Flatten headers config.headers = utils.merge( //Merge request headers into an array config.headers.common || {}, config.headers[config.method] || {}, config.headers || {} ); utils.forEach( //Delete again config.headers Li delete,get,head,post,put,patch,common Request header ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'], function cleanHeaderConfig(method) { delete config.headers[method]; } ); //The request header has been set up to execute here. var adapter = config.adapter || defaults.adapter; //Get the default configuration adapter,That's encapsulated. ajax Requester return adapter(config).then(function onAdapterResolution(response) { //implement adapter()Will send ajax Request.,then()The first parameter corrects the returned value throwIfCancellationRequested(config); // Transform response data response.data = transformData( //Call the default configuration transformResponse Processing the returned data response.data, response.headers, config.transformResponse ); return response; }, function onAdapterRejection(reason) { if (!isCancel(reason)) { throwIfCancellationRequested(config); // Transform response data if (reason && reason.response) { reason.response.data = transformData( reason.response.data, reason.response.headers, config.transformResponse ); } } return Promise.reject(reason); }); };
Finally, the function corresponding to the adapter attribute in the default configuration will be executed. Let's take a look at it, as follows:
function getDefaultAdapter() { //Get the default adapter,Namely Ajax Sender bar var adapter; // Only Node.JS has a process variable that is of [[Class]] process if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') { //For browsers, use XHR adapter // For node use HTTP adapter adapter = require('./adapters/http'); } else if (typeof XMLHttpRequest !== 'undefined') { //about node For the environment, use HTTP adapter // For browsers use XHR adapter adapter = require('./adapters/xhr'); } return adapter; } var defaults = { adapter: getDefaultAdapter(), //Adapter /*slightly*/ }
/ adapters/http is the implementation of the final ajax request. The main logic is as follows:
module.exports = function xhrAdapter(config) { //Send out XMLHTtpRequest()Request, etc. return new Promise(function dispatchXhrRequest(resolve, reject) { var requestData = config.data; var requestHeaders = config.headers; if (utils.isFormData(requestData)) { delete requestHeaders['Content-Type']; // Let the browser set it } var request = new XMLHttpRequest(); // HTTP basic authentication if (config.auth) { var username = config.auth.username || ''; var password = config.auth.password || ''; requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password); } request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true); //Initialization HTTP Asynchronous request invocation buildURL Obtain URL address // Set the request timeout in MS request.timeout = config.timeout; //Setting timeout time // Listen for ready state request.onreadystatechange = function handleLoad() { //binding onreadystatechange Event if (!request || request.readyState !== 4) { //If HTTP The response has not been received yet. return; //It returns directly without processing. } // 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)) { //Request error, no response logic if request.responseURL Not with file:Beginning and request.status=0,Return directly return; } // Prepare the response var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null; //Resolve the response header and call it parseHeaders Convert it to an object and save it to responseHeaders inside var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response; //If not set config.responseType Or set it up responseType.responseType And equal to text,Direct acquisition request.responseText,Otherwise get request.response var response = { //The patchwork of returned data, as mentioned in the previous article axios Return after request promise object data: responseData, //Received data status: request.status, //state ie The browser replaces port 204 with port 1223. See:https://github.com/axios/axios/issues/201 statusText: request.statusText, //Response Header Status Text headers: responseHeaders, //Head information config: config, //configuration information request: request //Corresponding XmlHttpRequest object }; settle(resolve, reject, response); //call settle Function to judge, yes resolve perhaps reject // Clean up request request = null; }; /*Abbreviated, mainly for error, timeout, some processing*/ // Add headers to the request if ('setRequestHeader' in request) { //If request In existence setRequestHeader utils.forEach(requestHeaders, function setRequestHeader(val, key) { //ergodic requestHeaders if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') { //If key Be equal to content-type And no data was sent. // Remove Content-Type if data is undefined delete requestHeaders[key]; //Delete content-type This request header ;Only when data is sent content-type Is it useful? } else { // Otherwise add header to the request request.setRequestHeader(key, val); //Otherwise, set the request header } }); } // Add withCredentials to request if needed if (config.withCredentials) { //Use credentials if cross-domain requests are set request.withCredentials = true; //Set up request.withCredentials by true } // Add responseType to request if needed if (config.responseType) { //If the data type of the server response is set, the default is json 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. if (config.responseType !== 'json') { throw e; } } } // Handle progress if needed if (typeof config.onDownloadProgress === 'function') { //If download processing progress events are set up request.addEventListener('progress', config.onDownloadProgress); } // Not all browsers support upload events if (typeof config.onUploadProgress === 'function' && request.upload) { //If upload processing progress events are set request.upload.addEventListener('progress', config.onUploadProgress); } if (config.cancelToken) { // Handle cancellation config.cancelToken.promise.then(function onCanceled(cancel) { if (!request) { return; } request.abort(); reject(cancel); // Clean up request request = null; }); } if (requestData === undefined) { //correct requestData,If so undefined,Is amended to null requestData = null; } // Send the request request.send(requestData); //send data }); };
That's the native ajax request, and the main logic is commented on, so the whole process runs out.
For convenient methods, such as axios.get() and axios.post(), it is an encapsulation of Axios.prototype.request. The implementation code is as follows:
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) { //Definition delete,get,head,options Method /*eslint func-names:0*/ Axios.prototype[method] = function(url, config) { return this.request(utils.merge(config || {}, { //call utils.merge Merge parameters into an object, and then call request()Method method: method, url: url })); }; }); utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) { //Definition post,put,patch Method /*eslint func-names:0*/ Axios.prototype[method] = function(url, data, config) { //call utils.merge Merge parameters into an object, and then call request()Method return this.request(utils.merge(config || {}, { method: method, url: url, data: data //post,put and patch than get Waiting for more requests data,The others are the same. })); }; });
OK, get it done.