I. Use of axios
The basic use of the axios
has been covered in the previous article and is reviewed here:
Send Request
import axios from 'axios';
axios(config) // Pass configuration directly
axios(url[, config]) // incomingurland configure
axios[method](url[, option]) // Directly call the request method,incomingurland configure
axios[method](url[, data[, option]]) // Directly call the request method,incomingdata、urland configure
axios.request(option) // transfer request method
const axiosInstance = axios.create(config)
// axiosInstance Also has the above axios Ability
axios.all([axiosInstance1, axiosInstance2]).then(axios.spread(response1, response2))
// transfer all and incoming spread callback
Request Blocker
axios.interceptors.request.use(function (config) {
// Write here the code for processing before sending the request
return config;
}, function (error) {
// Write here the code related to sending request errors
return Promise.reject(error);
});
Response Blocker
axios.interceptors.response.use(function (response) {
// Here is the code for post-processing of the response data.
return response;
}, function (error) {
// Here is the code to handle the error response
return Promise.reject(error);
});
Cancel Request
// method one
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('xxxx', {
cancelToken: source.token
})
// Cancel request (Request reason is optional)
source.cancel('Actively cancel the request');
// Method 2
const CancelToken = axios.CancelToken;
let cancel;
axios.get('xxxx', {
cancelToken: new CancelToken(function executor(c) {
cancel = c;
})
});
cancel('Actively cancel the request');
2. Implement a simple version of axios
Build a Axios
constructor with core code request
class Axios {
constructor() {
}
request(config) {
return new Promise(resolve => {
const {url = '', method = 'get', data = {}} = config;
// sendajaxask
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.onload = function() {
console.log(xhr.responseText)
resolve(xhr.responseText);
}
xhr.send(data);
})
}
}
Export axios
Instance
// final exportaxiosMethods,i.e. instancerequestmethod
function CreateAxiosFn() {
let axios = new Axios();
let req = axios.request.bind(axios);
return req;
}
// Get the final global variableaxios
let axios = CreateAxiosFn();
The above can already implement the request in this way axios({})
.
The following is to implement the request in this form axios.method()
.
// definitionget,post...method,hanging on toAxiosOn prototype
const methodsArr = ['get', 'delete', 'head', 'options', 'put', 'patch', 'post'];
methodsArr.forEach(met => {
Axios.prototype[met] = function() {
console.log('implement'+met+'method');
// Handle a single method
if (['get', 'delete', 'head', 'options'].includes(met)) { // 2parameters(url[, config])
return this.request({
method: met,
url: arguments[0],
...arguments[1] || {}
})
} else { // 3parameters(url[,data[,config]])
return this.request({
method: met,
url: arguments[0],
data: arguments[1] || {},
...arguments[2] || {}
})
}
}
})
Move the method on Axios.prototype
to request
Implement tool classes first, implement mixing b
methods into a
, and modify this
points to
const utils = {
extend(a,b, context) {
for(let key in b) {
if (b.hasOwnProperty(key)) {
if (typeof b[key] === 'function') {
a[key] = b[key].bind(context);
} else {
a[key] = b[key]
}
}
}
}
}
Modify the exported method
function CreateAxiosFn() {
let axios = new Axios();
let req = axios.request.bind(axios);
// Add code
utils.extend(req, Axios.prototype, axios)
return req;
}
Construct constructor for interceptor
class InterceptorsManage {
constructor() {
this.handlers = [];
}
use(fullfield, rejected) {
this.handlers.push({
fullfield,
rejected
})
}
}
Implement axios.interceptors.response.use
and axios.interceptors.request.use
class Axios {
constructor() {
// Add code
this.interceptors = {
request: new InterceptorsManage,
response: new InterceptorsManage
}
}
request(config) {
...
}
}
When the statements axios.interceptors.response.use
and axios.interceptors.request.use
are executed, the interceptors
object on the axios
instance is acquired, and then the response
or request
interceptor is acquired, and then the use
method of the corresponding interceptor is executed.
Move methods and attributes on Axios
to request
past
function CreateAxiosFn() {
let axios = new Axios();
let req = axios.request.bind(axios);
// mix-in method, deal withaxiosofrequestmethod,to possessget,post...method
utils.extend(req, Axios.prototype, axios)
// Add code
utils.extend(req, axios)
return req;
}
Now that request
also has interceptors
objects, the request
interceptor’s handlers
method is executed when the request is sent
First package the request to execute ajax
into a method
request(config) {
this.sendAjax(config)
}
sendAjax(config){
return new Promise(resolve => {
const {url = '', method = 'get', data = {}} = config;
// sendajaxask
console.log(config);
const xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.onload = function() {
console.log(xhr.responseText)
resolve(xhr.responseText);
};
xhr.send(data);
})
}
Get callback in handlers
request(config) {
// Interceptors and request assembly queues
let chain = [this.sendAjax.bind(this), undefined] // appearing in pairs,Failure callback is not processed temporarily
// request interception
this.interceptors.request.handlers.forEach(interceptor => {
chain.unshift(interceptor.fullfield, interceptor.rejected)
})
// response interception
this.interceptors.response.handlers.forEach(interceptor => {
chain.push(interceptor.fullfield, interceptor.rejected)
})
// execution queue,Execute one pair at a time,and givepromiseAssign the latest value
let promise = Promise.resolve(config);
while(chain.length > 0) {
promise = promise.then(chain.shift(), chain.shift())
}
return promise;
}
chains
roughly ['fulfilled1','reject1','fulfilled2','reject2','this.sendAjax','undefined','fulfilled2','reject2','fulfilled1','reject1']
this form
This makes it possible to successfully implement a simple version axios
III. Source Code Analysis
There are many ways to implement the send request, and the entry file is .
function createInstance(defaultConfig) {
var context = new Axios(defaultConfig);
// instancepointedrequestmethod,and the context points tocontext,So you can directly instance(option) method call
// Axios.prototype.request Internal judgment of the data type of the first parameter,enable us to instance(url, option) method call
var instance = bind(Axios.prototype.request, context);
// BundleAxios.prototypeThe method on extends toinstanceon object,
// and specify the context ascontext,Execute like thisAxiosmethod on the prototype chain,thiswill point tocontext
utils.extend(instance, Axios.prototype, context);
// Copy context to instance
// BundlecontextOwn properties and methods on the object extend toinstancesuperior
// Note:becauseextendfor internal useforEachmethod does something to an objectfor in When traversing,Only traverse the properties of the object itself,without traversing the properties on the prototype chain
// so,instance There it is defaults、interceptors Attributes。
utils.extend(instance, context);
return instance;
}
// Create the default instance to be exported Create a generated by default configurationaxiosExample
var axios = createInstance(defaults);
// Factory for creating new instances Expandaxios.createFactory function,inside too createInstance
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
// Expose all/spread
axios.all = function all(promises) {
return Promise.all(promises);
};
axios.spread = function spread(callback) {
return function wrap(arr) {
return callback.apply(null, arr);
};
};
module.exports = axios;
The main core is
// final exportaxiosMethods,i.e. instancerequestmethod
function CreateAxiosFn() {
let axios = new Axios();
let req = axios.request.bind(axios);
return req;
}
// Get the final global variableaxios
let axios = CreateAxiosFn();
```. The invocation of various request modes is implemented inside
```javascript
// final exportaxiosMethods,i.e. instancerequestmethod
function CreateAxiosFn() {
let axios = new Axios();
let req = axios.request.bind(axios);
return req;
}
// Get the final global variableaxios
let axios = CreateAxiosFn();
```. Look at the logic of
```javascript
// final exportaxiosMethods,i.e. instancerequestmethod
function CreateAxiosFn() {
let axios = new Axios();
let req = axios.request.bind(axios);
return req;
}
// Get the final global variableaxios
let axios = CreateAxiosFn();
```.
```javascript
Axios.prototype.request = function request(config) {
// Allow for axios('example/url'[, config]) a la fetch API
// judgment config Whether the parameter is string,If so, the first parameter is considered to be URL,The second argument is trueconfig
if (typeof config === 'string') {
config = arguments[1] || {};
// Bundle url place to config in object,Convenient for later mergeConfig
config.url = arguments[0];
} else {
// if config Whether the parameter is string,then the whole is regarded asconfig
config = config || {};
}
// Merge default configuration and incoming configuration
config = mergeConfig(this.defaults, config);
// Set request method
config.method = config.method ? config.method.toLowerCase() : 'get';
/*
something... This part will be discussed separately in the subsequent interceptor
*/
};
// exist Axios Mount on prototype 'delete', 'get', 'head', 'options' Request method without passing parameters,Implementation internals are also request
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
Axios.prototype[method] = function(url, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url
}));
};
});
// exist Axios Mount on prototype 'post', 'put', 'patch' And the request method of passing parameters,The same is true inside the implementation request
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
Axios.prototype[method] = function(url, data, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url,
data: data
}));
};
});
The entry parameter of request
is config
. It can be said that config
implements the lifetime of axios
.
In axios
, config
is mainly distributed in these places:
- Default configuration
defaults.js
config.method
defaults toget
- Call the
createInstance
method to create an instance ofaxios
, passed inconfig
- Direct or indirect invocation of
request
method, incomingconfig
// axios.js
// Create a generated by default configurationaxiosExample
var axios = createInstance(defaults);
// Expandaxios.createFactory function,inside too createInstance
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
// Axios.js
// Merge default configuration and incoming configuration
config = mergeConfig(this.defaults, config);
// Set request method
config.method = config.method ? config.method.toLowerCase() : 'get';
From the source code, you can see the priority: Instance attribute this.default
< request
parameter of the default configuration object default
< method:get
< Axios
Let’s focus on the
const utils = {
extend(a,b, context) {
for(let key in b) {
if (b.hasOwnProperty(key)) {
if (typeof b[key] === 'function') {
a[key] = b[key].bind(context);
} else {
a[key] = b[key]
}
}
}
}
}
``` approach
```javascript
Axios.prototype.request = function request(config) {
/*
First mergeConfig ... wait,no more elaboration
*/
// Hook up interceptors middleware Create interceptor chain. dispatchRequest is the top priority,Follow-up focus
var chain = [dispatchRequest, undefined];
// pushEach interceptor method Notice:interceptor.fulfilled or interceptor.rejected is possible forundefined
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
// Request interceptor reverse order Note here forEach It’s a custom interceptorforEachmethod
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
// Response interceptor order Note here forEach It’s a custom interceptorforEachmethod
chain.push(interceptor.fulfilled, interceptor.rejected);
});
// initialize apromiseobject,The status isresolved,The received parameters have been processed and mergedconfigobject
var promise = Promise.resolve(config);
// chain of loop interceptors
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift()); // Every time the interceptor pops out
}
// return promise
return promise;
};
Interceptor interceptors
is an instantiated property in build axios
function Axios(instanceConfig) {
this.defaults = instanceConfig;
this.interceptors = {
request: new InterceptorManager(), // request interception
response: new InterceptorManager() // response interception
};
}
InterceptorManager
constructor
// Initialization of interceptor In fact, it is a set of hook functions
function InterceptorManager() {
this.handlers = [];
}
// Call the interceptor instanceuseis to go to the hook functionpushmethod
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1;
};
// Interceptors can be canceled,according tousereturned whenID,Set an interceptor method tonull
// The ____ does not work splice or slice The reason is After deletion id will change,As a result, the subsequent sequence or operations are uncontrollable.
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null;
}
};
// This is where Axiosofrequestin method Mid-loop interceptor methods forEach Loop execution hook function
InterceptorManager.prototype.forEach = function forEach(fn) {
utils.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
}
The request interceptor method is unshift
into the interceptor, and the response interceptor is push
into the interceptor. Eventually they splice a method called dispatchRequest
to be executed in subsequent promise
sequences
var utils = require('./../utils');
var transformData = require('./transformData');
var isCancel = require('../cancel/isCancel');
var defaults = require('../defaults');
var isAbsoluteURL = require('./../helpers/isAbsoluteURL');
var combineURLs = require('./../helpers/combineURLs');
// Determine whether the request has been canceled,if it has been canceled,throw canceled
function throwIfCancellationRequested(config) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
}
module.exports = function dispatchRequest(config) {
throwIfCancellationRequested(config);
// if containsbaseUrl, and notconfig.urlabsolute path,combinationbaseUrlas well asconfig.url
if (config.baseURL && !isAbsoluteURL(config.url)) {
// combinationbaseURLandurlForm a complete request path
config.url = combineURLs(config.baseURL, config.url);
}
config.headers = config.headers || {};
// use/lib/defaults.jsneutraltransformRequestmethod,rightconfig.headersandconfig.dataFormat
// For example,headersneutralAccept,Content-TypeUniformly processed into uppercase letters
// For example, if the request body is aObjectwill be formatted asJSONstring,and addapplication/json;charset=utf-8ofContent-Type
// Wait for a series of operations
config.data = transformData(
config.data,
config.headers,
config.transformRequest
);
// Merge different configurationsheaders,config.headersThe configuration priority of
config.headers = utils.merge(
config.headers.common || {},
config.headers[config.method] || {},
config.headers || {}
);
// deleteheadersneutralmethodAttributes
utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);
// ifconfigConfiguredadapter,useconfigmedium placementadapterAn alternative to the default request method
var adapter = config.adapter || defaults.adapter;
// useadapterMethod to initiate a request(adapterDepending on the browser environment orNodeThe environment will be different)
return adapter(config).then(
// Request a callback that returns correctly
function onAdapterResolution(response) {
// Determine whether and cancel the request,If the request is canceled throw to cancel
throwIfCancellationRequested(config);
// use/lib/defaults.jsneutraltransformResponsemethod,Format the data returned by the server
// For example,useJSON.parseParse the response body
response.data = transformData(
response.data,
response.headers,
config.transformResponse
);
return response;
},
// Request failed callback
function onAdapterRejection(reason) {
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
if (reason && reason.response) {
reason.response.data = transformData(
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(reason);
}
);
};
See how
function CreateAxiosFn() {
let axios = new Axios();
let req = axios.request.bind(axios);
// Add code
utils.extend(req, Axios.prototype, axios)
return req;
}
``` implements the cancellation request. The implementation file is in
```javascript
function CreateAxiosFn() {
let axios = new Axios();
let req = axios.request.bind(axios);
// Add code
utils.extend(req, Axios.prototype, axios)
return req;
}
```.
```javascript
function CancelToken(executor) {
if (typeof executor !== 'function') {
throw new TypeError('executor must be a function.');
}
// exist CancelToken Define a pending status promise ,Will resolve Callback assigned to external variable resolvePromise
var resolvePromise;
this.promise = new Promise(function promiseExecutor(resolve) {
resolvePromise = resolve;
});
var token = this;
// Execute immediately incoming executorfunction,will be true cancel Methods are passed through parameters。
// Execute once called resolvePromise That is, the previous promise of resolve,Just changepromiseThe status is resolve。
// Soxhrdefined in CancelToken.promise.thenThe method will be executed, therebyxhrThe request will be canceled internally
executor(function cancel(message) {
// Determine whether the request has been canceled,Avoid multiple executions
if (token.reason) {
return;
}
token.reason = new Cancel(message);
resolvePromise(token.reason);
});
}
CancelToken.source = function source() {
// source The method returns a CancelToken Example,with direct use new CancelToken It's the same operation
var cancel;
var token = new CancelToken(function executor(c) {
cancel = c;
});
// Return the created CancelToken Examples and cancellation methods
return {
token: token,
cancel: cancel
};
};
The action that canceled the request was actually matched by a response in xhr.js
if (config.cancelToken) {
config.cancelToken.promise.then(function onCanceled(cancel) {
if (!request) {
return;
}
// Cancel request
request.abort();
reject(cancel);
});
}
In CancelToken
, the function controls the state of promise
through the transfer and execution of the function.
From https://blog.csdn.net/weixin_50367873/article/details/135642707,If there is any infringement,Please contact to delete。