This is the first day of my participation in the More text Challenge. For details, see more text Challenge
preface
Axios believes that the iron juice familiar with Vue will not be unfamiliar to it (of course, you can also recognize it if you are not familiar with Vue), which is a big killer in the front end in recent years. Since the start of Vue2, the official recommendation to use Axios to make network requests, after the basic majority of Vue projects can see its figure.
Now we don’t have to say much, directly start today’s topic, although axios is very strong, but pure Axios is not enough for our daily use, so many times we need to double encapsulate AxiOS, next we will discuss in detail.
The preparatory work
The front end
Vite a super super super fast development artifact, is still yu Yu creek big masterpiece, this time to use Vite to initialize the project to complete the coding.
Direct initialization project for more details.
npm init @vitejs/app
Copy the code
Download the Axios dependency.
npm install axios
Copy the code
The back-end
Borrow node to build a simple server. The reason why I build a service by myself instead of finding an interface request on the Internet is also for the convenience of verifying some special cases, such as request timeout, different HTTP status codes, data structures of various responses, and so on.
Create a service directory in the initial project directory, and click here to set up the details of the service.
Then we start the service with the node app.js command, and we have three interfaces that look like this:
- http://localhost:8888/api/register
- http://localhost:8888/api/login
- http://localhost:8888/api/list
Independent API management
With the above preparations in place, we can begin to get down to business. Unified management of all API interfaces of a project is very important, so as to facilitate the later update and maintenance. Therefore, we separate API layer to manage all APIS of the project, and divide the file to which each API belongs by module.
Js file is created to encapsulate AXIos twice. Other files are functional modules in the corresponding project. For example, all the API related to goods are put in the goods.js file, and all the API related to orders are put in the order.js file. It’s very organized.
First, let’s simply write axios.js.
import axios from 'axios'; Function myAxios(axiosConfig) {const service = axios.create({baseURL: 'http://localhost:8888', // set the uniform request prefix timeout: 10000, // set a uniform timeout period}); return service(axiosConfig) } export default myAxios;Copy the code
Note that service(axiosConfig) returns a Promise object. (The reason why this is a function is for subsequent encapsulation operations, which will be covered later)
Let’s write an API for retrieving the list of goods in goods.js.
import myAxios from './axios';
export function getListAPI(paramsList) {
return myAxios({
url: '/api/list',
method: 'get',
})
}
Copy the code
If the request is an absolute path, it can also be entered in the URL parameter. The baseUrl parameter is not prefixed, which is a characteristic of the baseUrl parameter.
Finally, we will use it in the page. Add a button randomly in the app. vue file and click to trigger the request.
<template>
<button @click="getList">点击</button>
</template>
<script lang='ts'>
import {defineComponent} from 'vue'
import {getListAPI} from '@/api/goods.js';
export default defineComponent({
setup() {
function getList() {
getListAPI().then(res => {
console.log(res)
})
}
return {
getList
}
}
})
</script>
Copy the code
Here we simply divide the API management layer. Every time we add an API, we just need to find the API file of the corresponding module and import it in the specific page. You can use the xxxAPI end to mark API methods in case they are mixed with normal methods.
$axios.getList(); $axios.getList(); $axios.getList(); If the project is large and requires a large number of apis, it is easy to get confused and it is not easy to classify apis. When using this, we also need to consider the problem of pointing to this. Vue3 has no such thing as this.
Serialization of POST request parameters
The content-type in a POST request has the following three forms:
- Content-Type: application/json
- Content-Type: application/x-www-form-urlencoded
- Content-Type: multipart/form-data
Nowadays, Axios works in the application/ JSON format by default. It’s easy to pass parameters to the back-end interface, just drop them in the data parameter. We write the login API in the user.js file
import myAxios from './axios';
export function login(paramsList) {
return myAxios({
url: '/api/login',
method: 'post',
data: paramsList
});
}
Copy the code
In the specific page import call this method to pass the relevant parameters.
But sometimes the backend requires that the content-type must be in the form of Application/X-wwW-form-Urlencoded, so through the parameters passed above, the back end is not received, we have to serialize the parameter data. Send it to the back end as a normal form (key-value pairs), not as JSON. More on serializing content is up to you, but here’s how to do it.
// user.js
import myAxios from './axios';
export function loginAPI(paramsList) {
return myAxios({
url: '/api/login',
method: 'post',
data: paramsList,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
transformRequest: [
(data) => {
let result = ''
for (let key in data) {
result += encodeURIComponent(key) + '=' + encodeURIComponent(data[key]) + '&'
}
return result.slice(0, result.length - 1)
}
],
});
}
Copy the code
I use headers to specify the content-type form. For transformRequest, I allow you to modify the request data before sending it to the server, but only for ‘PUT’, ‘POST’, and ‘PATCH’ request methods. The later functions in the array must return a string, or an ArrayBuffer, or a Stream. Moreover, the transformResponse is allowed to modify the response data before being passed to the then/catch. For more parameters, see the Axios documentation.
In this case, you can configure the Axios configuration separately for a single request, but it is easier to customize the Loading.
Finally, click the View Source in the red box in the figure through the browser network to see the serialized parameter form.
Serialize the parameters using the QS module
We can also serialize parameters through third-party dependencies, which is more convenient and concise. Download the QS module.
npm install qs
Copy the code
// user.js
import qs from 'qs';
export function loginAPI(paramsList) {
return myAxios({
url: '/api/login',
method: 'post',
data: paramsList,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
transformRequest: [
(data) => {
return qs.stringify(data)
}
],
});
}
Copy the code
Cancel duplicate request
Speaking of this repeated request, I feel used less, there is always a psychological idea even if more requests once and how, the server will collapse? Will the page hang up? Obviously not. Don’t be surprised. Hahaha. Besides, why would there be so many repeated requests? It’s impossible.
And do cancel repeated request operation, in fact, after the cancellation of the request is likely to reach the back end, but the front-end browser does not deal with it, but, alas, we still have to do work, no, must do, so called in case, rigorous, procedural monkey needs rigorous!!
There are two common scenarios in which duplicate requests occur:
- Clicking a button in quick succession, if the button is not controlled, will make a repeat request, assuming that the request is to generate an order, then two orders will be generated, which is a terrible thing. Of course, the front end usually has some state control over this button, and the back end has some idempotent control or something, so this is a hypothetical scenario, but it could happen.
- For list data, there may be frequent switching queries in the TAB status bar, which can also generate repeated requests if the request response is slow. Of course, many lists are now cached, such as Vue
<keep-alive />
.
How do I cancel a sent request
Before we get started, we need to know how to cancel a request that has already been sent. All you need to know is that axios depends on it at the bottom, which is its second wrapper. Why don’t we wrap Axios again, which is the third wrapper? Dolls?
The XMLHttpRequest object is where we start a network request. Underneath it is the method.abort(), which breaks a request that has already been made.
Axios also has a related encapsulation, called CancelToken, which is described in the documentation:
var CancelToken = axios.CancelToken; var cancel; axios.get('/user/12345', { cancelToken: New CancelToken(function executor(c) {// cancel = c; })}); // Cancel the request cancel();Copy the code
New Axios.cancelToken () gives each request a unique CancelToken, and then receives a cancel() method for subsequent cancelations, so we need to store this method accordingly.
Began to
With the above understanding, we can enter into the main part of the next, the general idea is to collect the interface in the request, that is, the interface state or pending state, and let them form a queue for storage. If the same interface is triggered again, the interface in the request is cancelled and deleted from the queue. Then the request is re-initiated and stored in the queue. If the interface returns a result, the process is deleted from the queue.
Identify duplicate requests and store them in the queue
First we need to collect the interfaces in the requests and determine which requests are duplicate so that we can cancel them. How? Very simple, as long as the request address, request mode, request parameters are the same, then we can think of the same. The data structure in the queue that we are storing should obviously be in the form of key-value pairs, which we select the Map object to operate on.
// axios.js const pendingMap = new Map(); /** * generate a unique key for each request * @param {*} config * @returns string */ function pendingKey (config) {let {url, method, params, data} = config; if(typeof data === 'string') data = JSON.parse(data); Return [url, method, json.stringify (params), json.stringify (data)]. Join ('&'); } /** * stores a unique value for each request, i.e., the cancel() method, Function addPending(config) {const pendingKey = getPendingKey(config); config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => { if (! pendingMap.has(pendingKey)) { pendingMap.set(pendingKey, cancel); }}); }Copy the code
Cancel duplicate requests and exit the delete queue
// axios.js /** * Remove duplicate requests * @param {*} config */ function removePending(config) {const pendingKey = getPendingKey(config); if (pendingMap.has(pendingKey)) { const cancelToken = pendingMap.get(pendingKey); cancelToken(pendingKey); pendingMap.delete(pendingKey); }}Copy the code
Adding interceptors
// axios.js function myAxios(axiosConfig) { const service = axios.create({ baseURL: 'http://localhost:8888', // set the uniform request prefix timeout: 10000, // set the uniform timeout period}); service.interceptors.request.use( config => { removePending(config); addPending(config); return config; }, error => { return Promise.reject(error); }); service.interceptors.response.use( response => { removePending(response.config); return response; }, error => { error.config && removePending(error.config); return Promise.reject(error); }); return service(axiosConfig) }Copy the code
Let’s simply simulate making three repeated requests in a row in the getList() method mentioned above, and then set the browser to 3G mode to see the effect.
// App.vue function getList() { getListAPI().then(res => {console.log(res)}) setTimeout(() => { getListAPI().then(res => {console.log(res)}) }, 200); setTimeout(() => { getListAPI().then(res => {console.log(res)}) }, 400); }Copy the code
It’s important to note that it saysCancel the interface being requested“Indicates that the interface may have reached the back end, but the back end is slow, so if you have a fast interface, it’s hard to see the effect. If you build your own service, you can see the effect by simply returning the delay through the interface. Or you can adjust the network speed through the browser’s Network.
We should also have a reasonable handling of cancelled requests, not just ignore them, as far as possible to the controllable bottom of the code, it will be classified as exceptions, as described below (^ω^).
Configuration change
The reason why it is configured to cancel repeated requests, because there may be some special abnormal scenarios, is the need for repeated requests, such as input real-time search, real-time update data, and so on, but it is possible. ( ̄y▽ ̄)~*
// axios.js function myAxios(axiosConfig, customOptions) { const service = axios.create({ baseURL: 'http://localhost:8888', // set the uniform request prefix timeout: 10000, // set the uniform timeout period}); Let custom_options = object. assign({repeat_request_cancel: true, // whether to enable cancel repeated requests, default is true}, customOptions); service.interceptors.request.use( config => { removePending(config); custom_options.repeat_request_cancel && addPending(config); return config; }, error => { return Promise.reject(error); }); . }Copy the code
We added a custom configuration parameter to the above. Now each API method has two parameters. The first parameter passes some of the original axios configuration, and the second parameter is our own custom parameter. For example, we define repeat_request_cancel to control whether the function of canceling repeated requests is enabled. We will be able to add more features in the future, which is equivalent to customizing every API method, isn’t it great!!
// goods.js
export function getListAPI(paramsList) {
return myAxios({
url: '/api/list',
method: 'get',
params: paramsList
}, {
repeat_request_cancel: false
})
}
Copy the code
Loading
Asynchronous data is a very common scenario. A good Loading effect can enhance user experience and avoid some problems, such as the repeated request mentioned above. If a Loading layer appears immediately after a request is initiated, the user cannot click again, resulting in repeated requests.
To add a feature we need to consider how to do three things:
- If multiple requests are initiated at the same time, we only need to display one Loading layer.
- A Loading layer that initiates multiple requests at the same time will disable destruction in response to the last request.
- This feature is still configurable.
Without further further, let’s play with ElementPlus’s Loading effect and check out the code’s comments.
// axios.js const LoadingInstance = {_target: null, // Save Loading instance _count: 0}; function myAxios(axiosConfig, customOptions) { const service = axios.create({ baseURL: 'http://localhost:8888', // set the uniform request prefix timeout: 10000, // set the uniform timeout period}); Let custom_options = object. assign({repeat_request_cancel: true, // Whether to enable cancel repeated requests, default is true loading: False, // Whether to enable loading layer effect, default is false}, customOptions); service.interceptors.request.use( config => { removePending(config); custom_options.repeat_request_cancel && addPending(config); // Create a loading instance if (custom_options.loading) {loadingInstance._count ++; if(LoadingInstance._count === 1) { LoadingInstance._target = ElLoading.service(); } } return config; }, error => { return Promise.reject(error); }); service.interceptors.response.use( response => { removePending(response.config); custom_options.loading && closeLoading(custom_options); // Close loading return response; }, error => { error.config && removePending(error.config); custom_options.loading && closeLoading(custom_options); // Close loading return promise.reject (error); }); Return service(axiosConfig)} /** * closeLoading layer instance * @param {*} _options */ function closeLoading(_options) { if(_options.loading && LoadingInstance._count > 0) LoadingInstance._count--; if(LoadingInstance._count === 0) { LoadingInstance._target.close(); LoadingInstance._target = null; }}Copy the code
If two requests have been initiated at the same time, and both of them have a Loading layer open, and now one of them has terminated and the Loading layer is closed, but the other one has not terminated and is still Loading for some reason, The result is that the loading layer is closed before the page request is completed, and the user will think that the page is loaded. As a result, the page does not work properly, resulting in a bad user experience. Therefore, a variable is added to record the number of requests.
Of course, if you have a Loading layer, you might think that if the interface has a long response time and the data is not affecting the rest of the page, then having a Loading layer is a bad experience. Fortunately, I have Plan B, so designing the Loading layer above is a configurable option. In this case, the API can choose not to use the Loading layer at the page level and instead use the Loading layer at the element level itself.
For the Loading component of ElementPlus, it also has a number of configuration parameters.
We can also customize it, but for simplicity and simplicity we’ll just add a third parameter.
// axios.js function myAxios(axiosConfig, customOptions, loadingOptions) { service.interceptors.request.use( config => { ... // Create a loading instance if (custom_options.loading) {loadingInstance._count ++; if(LoadingInstance._count === 1) { LoadingInstance._target = ElLoading.service(loadingOptions); } } return config; },...). ; . }Copy the code
At this point, we can customize a Loading layer for each API method that requires a Loading layer at the page level. (Isn’t it great and fancy? Ha, ha, ha)
// goods.js export function getListAPI(paramsList) { return myAxios({ url: '/api/list', method: 'get', params: ParamsList}, {loading: true}, {text: 'Get list data.... '})}Copy the code
Determine different HTTP status codes
A good display of the real-time status of the interface prompt information is very important. During development, it is convenient for front-end personnel to locate problems, during testing, it is convenient for testers to notify corresponding personnel, and in some complex special scenarios, users can be prompted and guided.
Before starting the code, let’s print several interface exceptions and test the code as follows:
// axios.js function myAxios(axiosConfig, customOptions, loadingOptions) { ... service.interceptors.response.use( ... error => { ... httpErrorStatusHandle(error); // Handle the error status code return promise.reject (error); // The error continues to be returned to the specific page}); . Function httpErrorStatusHandle(error) {console.log('error: ', error);} / / httpErrorStatusHandle(error) {console.log('error: ', error); console.log('error.message: ', error.message); console.log('error.response: ', error.response); }Copy the code
Note that the error printed above is an object in nature, but the console may not be obvious, and there are a number of properties below that we can use, as the documentation says.
Various abnormalities
The back-end throws a fault, and the client is disconnected
In this case, the interface is usually down, and the client is disconnected from the Internet. We can use window.navigator. OnLine to determine whether the Internet is disconnected.
// app.js app.get('/api/list', (req, res) => { // console.log(a); Throw new Error(' Error!! '); });Copy the code
The request timeout
We change the Node service delay response to create the timeout effect
// app.js app.get('/api/list', (req, res) => { setTimeout(() => { res.end(); }, 150000)});Copy the code
3 xx redirection
4XX Client error
5XX The server is incorrect
// app.js app.get('/api/list', (req, res) => { // res.statusCode = 302; // res.end(' redirect '); // res.statusCode = 400; // res.end(' request parameter error '); res.statusCode = 500; Res.end (' server internal error '); });Copy the code
The above is a general list of common situations, below I will directly on the code, it is quite simple, as long as the interface error, prompt the corresponding error message (=^▽^=).
Specific coding
// Axios.js /** * Handle exception * @param {*} error */ function httpErrorStatusHandle(error) {// Handle cancelled request If (axios.iscancel (error)) return console.error(' Requested duplicate request: '+ error.message); let message = ''; If (error && error.response) {switch(err.response.status) {case 302: message = 'interface redirected! '; break; Case 400: message = 'error! '; break; Case 401: message = 'You are not logged in, or login has timed out, please log in first! '; break; Case 403: message = 'You have no permission to operate! '; break; Case 404: error message = ` request address: ${error. The response. Config. Url} `; break; Case 408: message = 'request timed out! '; break; Case 409: message = 'the same data already exists in the system! '; break; Case 500: message = 'Internal server error! '; break; Case 501: message = 'service not implemented! '; break; Case 502: message = 'Gateway error! '; break; Case 503: message = 'Service unavailable! '; break; Case 504: message = 'service cannot be accessed, please try again later! '; break; Case 505: message = 'HTTP version not supported! '; break; Default: message = 'Exception problem, please contact administrator! '; Break}} if (err.message.includes ('timeout')) message = 'Network request timeout! '; if (error.message.includes('Network')) message = window.navigator.onLine ? 'Server exception! ':' You are disconnected! '; ElMessage({ type: 'error', message }) }Copy the code
It’s not hard to write this exception handling method, but let’s be careful to handle the case of cancellations mentioned above. The cancelled request will also be included in this case, and we will simply print the interface for the repeated request on the console.
We use the Message component of ElementPlus to prompt messages. The prompt copy can change or add more cases, or the interface can be used to define the Message, depending on the case.
Of course, we also configure this function:
// axios.js function myAxios(axiosConfig, customOptions, loadingOptions) { ... Let custom_options = object.assign ({... Error_message_show: true, // Whether to enable the display of interface error information. The default value is true}, customOptions); service.interceptors.response.use( ... error => { ... custom_options.error_message_show && httpErrorStatusHandle(error); // Handle the error status code return promise.reject (error); // The error continues to be returned to the specific page}); . }Copy the code
Other useful little optimizations
Request to carry the token automatically
It’s a little bit easier to just go straight to the code.
// axios.js import {getTokenAUTH} from '@/utils/auth'; function myAxios(axiosConfig, customOptions, loadingOptions) { ... service.interceptors.request.use( config => { ... If (getTokenAUTH() && Typeof Window! == "undefined") { config.headers.Authorization = getTokenAUTH(); } return config; },...). ; . }Copy the code
// auth.js
const TOKEN_KEY = '__TOKEN';
export function getTokenAUTH() {
return localStorage.getItem(TOKEN_KEY);
}
Copy the code
My token is stored in the local cache. If your token is stored in the store, you can change it. Don’t tell me you don’t know how to change it. typeof window ! == “undefined” is mainly for compatibility with SSR environment.
Concise data response structure
// axios.js function myAxios(axiosConfig, customOptions, loadingOptions) { ... Let custom_options = object. assign({repeat_request_cancel: true, // Whether to enable cancel repeated requests, default is true loading: Reduct_data_format: true, // Whether to enable concise data structure response, default: true}, customOptions); . service.interceptors.response.use( response => { removePending(response.config); custom_options.loading && closeLoading(custom_options); // Close loading return custom_options.reduct_data_format? response.data : response; },...). ; . }Copy the code
The response data returned by AXIos by default will help us wrap up a layer of data, but the real data returned by the backend is in Response. data, so sometimes we need to ask back data for a long time to access, if one of the middle layer is broken, it is easy to cause an error. So we can set the return point concise data directly to the specific page logic, easy to use, throughreduct_data_format
Parameter to control the configuration.
Of course, the above is simply shortening the outermost layer of Axios, but if you already have an explicit and company-specific response data structure, you can modify it again, depending on the scenario.
Error message about code
This point should be based on the specific business scenario!! Most of the time, the back-end interface will define a code parameter in addition to the HTTP status code to determine whether the current interface is “normal”. Normally, the code will be equal to 0.
function myAxios(axiosConfig, customOptions, loadingOptions) { ... let custom_options = Object.assign({ ... Code_message_show: false, // Whether to enable the message prompt when code is not 0, default is false}, customOptions); service.interceptors.response.use( response => { ... if(custom_options.code_message_show && response.data && response.data.code ! == 0) { ElMessage({ type: 'error', message: response.data.message }) return Promise.reject(response.data); } return custom_options.reduct_data_format? response.data : response; },...). ; . }Copy the code
In simple terms, when code is not equal to zero, we will directly display the prompt brought by the back end, of course, this needs to be discussed between the front and back end, fixed data structure of the response, and in the specific page logic we will only deal with the normal case when code is equal to zero. If you define the data structure on the front and back ends, you can change the code_message_show default value. This is a very convenient function without having to enable each interface.
The complete code
Finally give axios.js complete code, liver for two days, write tired, hope to help you.
import axios from 'axios'; import { ElLoading, ElMessage } from 'element-plus'; import {getTokenAUTH} from '@/utils/auth'; const pendingMap = new Map(); const LoadingInstance = { _target: null, _count: 0 }; function myAxios(axiosConfig, customOptions, loadingOptions) { const service = axios.create({ baseURL: 'http://localhost:8888', // set the uniform request prefix timeout: 10000, // set the uniform timeout period}); Let custom_options = object. assign({repeat_request_cancel: true, // Whether to enable cancel repeated requests, default is true loading: Reduct_data_format: true. Error_message_show: whether to enable the loading layer effect. The default value is false. Code_message_show: false, // Whether to enable the message display when code is not 0, the default value is false}, customOptions); / / request intercept service interceptors. Request. Use (config = > {removePending (config); custom_options.repeat_request_cancel && addPending(config); // Create a loading instance if (custom_options.loading) {loadingInstance._count ++; if(LoadingInstance._count === 1) { LoadingInstance._target = ElLoading.service(loadingOptions); If (getTokenAUTH() && Typeof Window! == "undefined") { config.headers.Authorization = getTokenAUTH(); } return config; }, error => { return Promise.reject(error); }); / / response to intercept service. Interceptors. Response. Use (response = > {removePending (response. Config); custom_options.loading && closeLoading(custom_options); // Close loading if(custom_options.code_message_show && Response.data && Response.data.code! == 0) { ElMessage({ type: 'error', message: response.data.message }) return Promise.reject(response.data); } return custom_options.reduct_data_format? response.data : response; }, error => { error.config && removePending(error.config); custom_options.loading && closeLoading(custom_options); // Close loading custom_options.error_message_show && httpErrorStatusHandle(error); // Handle the error status code return promise.reject (error); // The error continues to be returned to the specific page}); return service(axiosConfig) } export default myAxios; Function httpErrorStatusHandle(error) {// Handle canceled requests if(axios.iscancel (error)) return Console. error(' requested duplicate request: '+ error.message); let message = ''; If (error && error.response) {switch(err.response.status) {case 302: message = 'interface redirected! '; break; Case 400: message = 'error! '; break; Case 401: message = 'You are not logged in, or login has timed out, please log in first! '; break; Case 403: message = 'You have no permission to operate! '; break; Case 404: error message = ` request address: ${error. The response. Config. Url} `; break; Case 408: message = 'request timed out! '; break; Case 409: message = 'the same data already exists in the system! '; break; Case 500: message = 'Internal server error! '; break; Case 501: message = 'service not implemented! '; break; Case 502: message = 'Gateway error! '; break; Case 503: message = 'Service unavailable! '; break; Case 504: message = 'service cannot be accessed, please try again later! '; break; Case 505: message = 'HTTP version not supported! '; break; Default: message = 'Exception problem, please contact administrator! '; Break}} if (err.message.includes ('timeout')) message = 'Network request timeout! '; if (error.message.includes('Network')) message = window.navigator.onLine ? 'Server exception! ':' You are disconnected! '; ElMessage({ type: 'error', Message})} /** * Close the Loading layer instance * @param {*} _options */ function closeLoading(_options) {if(_options.loading && LoadingInstance._count > 0) LoadingInstance._count--; if(LoadingInstance._count === 0) { LoadingInstance._target.close(); LoadingInstance._target = null; }} /** * store a unique cancel callback for each request, * @param {*} config */ function addPending(config) {const pendingKey = getPendingKey(config); config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => { if (! pendingMap.has(pendingKey)) { pendingMap.set(pendingKey, cancel); }}); } /** * Delete duplicate requests * @param {*} config */ function removePending(config) {const pendingKey = getPendingKey(config); if (pendingMap.has(pendingKey)) { const cancelToken = pendingMap.get(pendingKey); cancelToken(pendingKey); pendingMap.delete(pendingKey); }} /** * generate a unique key for each request * @param {*} config * @returns */ function pendingKey (config) {let {url, method, params, data} = config; if(typeof data === 'string') data = JSON.parse(data); Return [url, method, json.stringify (params), json.stringify (data)]. Join ('&'); }Copy the code
At this point, this article is finished, flower flower.
I hope this article has been helpful to you and look forward to your comments if you have any questions. Same old, like + comment = you got it, favorites = you got it.