preface

  • Music Blog is online!
  • What is Axios? What are the characteristics? Request and response interceptor?
  • The following is a full introduction to Axios related knowledge (including the core principle and compatibility of handwriting)





The core principles of Handwriting Axios

What is Axios?

Axios is a Promise based HTTP library that runs in a browser, Node environment.

What features does Axios have? (For an interview)

  • Create XMLHttpRequests from the browser
  • Create HTTP requests from Node
  • Supporting Promise API
  • Intercept requests and responses
  • Transform request data and response data
  • Cancel the request
  • Automatically convert Json data
  • The client supports XSRF defense

In fact, AXIOS works in browsers and Node.js because it automatically determines what the current environment is and implements AXIOS based on XMLHttpRequests if it is a browser. In a Node.js environment, axiOS is implemented based on the node built-in core module HTTP

In a nutshell, the rationale behind Axios is this

  • Axios is still an XMLHttpRequest, so you need to implement an Ajax. Or based on HTTP.
  • You also need a Promise object to process the results.

Basic usage mode

The basic ways to use Axios are as follows

  • axios(config)
  • axios.method(url, data, config)

MyAxios. Js file

/ / index. The HTML file
<html>
<script type="text/javascript" src="./myaxios.js"></script>
<body>
<button class="btn">Click me to send request</button>
<script>
    document.querySelector('.btn').onclick = function() {
        // Use the following method calls respectively to see the effect of myaxios
        axios.post('/postAxios', {
          name: 'little beauty post'
        }).then(res= > {
          console.log('postAxios responded successfully ', res);
        })

        axios({
          method: 'post'.url: '/getAxios'
        }).then(res= > {
          console.log('getAxios responded successfully ', res); })}</script>
</body>
</html>
</html>
Copy the code

Implement Axios and axios.method

You can see from the use of axios(config) that the exported axios is a method. The use of axios.method(URL, data, config) shows that the exported axios or prototype has methods like GET, POST, etc.

The exported AXIos is actually a method in an AXIos class.

So the core code is Request. We export request so we can call axios in the form of axios(config).

class Axios {
    constructor(){}request(config) {
        return new Promise(resolve= > {
            const {url = ' ', method = 'get', data = {}} = config;
            // Send an Ajax request
            const xhr = new XMLHttpRequest();
            xhr.open(method, url, true);
            xhr.onload = function() {
                console.log(xhr.responseText) resolve(xhr.responseText); } xhr.send(data); }}})Copy the code

How do I derive that? Very simple, new Axios, get an instance of Axios, and then get the request method on that instance.

// Finally export the axios method, the instance's request method
function CreateAxiosFn() {
    let axios = new Axios();
    let req = axios.request.bind(axios);
    return req;
}

// Get the last global variable axios
let axios = CreateAxiosFn();
Copy the code

Now axios is actually a request method.

You might be wondering, because I was wondering when I looked at the source code: Why not just write a request method and export it? It’s got to be a big detour. Don’t worry. We’ll get to that later.

Now that we have a simple axios, let’s import the myaxios.js file and test it to see if it works.

//index.html
<script type="text/javascript" src="./myAxios.js"></script>

<body>
<button class="btn">Click me to send request</button>
<script>
    document.querySelector('.btn').onclick = function() {
        // Use the following method calls respectively to see the effect of myaxios
        axios({
          method: 'get'.url: 'https://zhengzemin.cn/dist/static/fonts/element-icons.535877f.woff'
        }).then(res= > {
          console.log('getAxios responded successfully ', res); })}</script>
</body>
Copy the code

Click the button to see if you can successfully get the data.

Well, it’s perfect. The browser prints a bunch of information that we requested back through Axios

Now let’s implement the form axios.method().

Train of thought. We can add these methods to axios.prototype. These methods call the Request method internally, as shown in the code:

/ / define the get and post... Method, hang onto the Axios prototype
const methodsArr = ['get'.'delete'.'head'.'options'.'put'.'patch'.'post'];
methodsArr.forEach(met= > {
    Axios.prototype[met] = function() {
        console.log('execution'+met+'methods');
        // Process a single method
        if (['get'.'delete'.'head'.'options'].includes(met)) { // 2 parameters (url[, config])
            return this.request({
                method: met,
                url: arguments[0],
                ...arguments[1) | | {}})}else { // 3 parameters (url[,data[,config]])
            return this.request({
                method: met,
                url: arguments[0].data: arguments[1) | | {},... arguments[2) | | {}}}}})Copy the code

We add the corresponding methods to axios. prototype by iterating through the methodsArr array, noting that ‘get’, ‘delete’, ‘head’, and ‘options’ only take two arguments. Others take three arguments. Get does not put arguments in the body. But what if we just added the corresponding method to Axios prototype and exported the request method? Simple, just move the axios.prototype method to Request. Let’s first implement a tool method that mixes B’s methods with A;

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]
        }
      }
      
    }
  }
}
Copy the code

We can then use this method to move the axios.prototype method to request.

Let’s modify the CreateAxiosFn method

function CreateAxiosFn() {
  let axios = new Axios();
  
  letreq = axios.request.bind(axios); Add code utils.extend(req, axios.prototype, Axios)return req;
}
Copy the code

Now let’s test if we can call axios using the form axios.get().

<body>
<button class="btn">Click me to send request</button>
<script>
    document.querySelector('.btn').onclick = function() {

        axios.get('https://zhengzemin.cn/dist/static/fonts/element-icons.535877f.woff')
            .then(res= > {
                 console.log('getAxios responded successfully ', res); })}</script>
</body>
Copy the code

It was an unexpected success.

Request and response interceptors

Let’s look at the use of interceptors

// Add request interceptor
axios.interceptors.request.use(function (config) {
    // What to do before sending the request
    return config;
  }, function (error) {
    // What to do about the request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // What to do with the response data
    return response;
  }, function (error) {
    // Do something about the response error
    return Promise.reject(error);
  });
Copy the code

What does interceptor mean? When we send a request, we execute the request interceptor code, and then we actually execute the request. This process does something to the config, the parameters we pass when we send the request.

When a response is received, the code of the response interceptor is executed, and then the data of the response is returned. This process performs a series of operations on the data of the response.

How do you do that? To be clear, an interceptor is also a class that manages responses and requests. So let’s implement the interceptor first

class InterceptorsManage {
  constructor() {
    this.handlers = [];
  }

  use(fullfield, rejected) {
    this.handlers.push({
      fullfield,
      rejected
    })
  }
}
Copy the code

We are using this statement axios. Interceptors. Response. Use and axios interceptors. Request. The use, to trigger the interceptors use method of execution.

Note axios has a response interceptor and a request interceptor. So how do you implement Axios? Look at the code

class Axios {
    constructor(){new codethis.interceptors = {
            request: new InterceptorsManage,
            response: new InterceptorsManage
        }
    }

    request(config) {
        return new Promise(resolve= > {
            const {url = ' ', method = 'get', data = {}} = config;
            // Send an Ajax request
            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); }}})Copy the code

As you can see, there is an object interceptors on the AXIos instance. This object has two interceptors, one for handling requests and one for handling responses.

So, we execute a statement axios. Interceptors. Response. Use and axios interceptors. Request. The use of time, implement axios instance the interceptors on the object, The Response or Request interceptor is then retrieved and the corresponding interceptor’s use method is executed.

Using the Use method pushes the callback we passed into the handlers array of the interceptor.

Do you see a problem here? The interceptors object comes from Axios, and we export the request method. Familiar question, mentioned above hahaha ~~~ ugh). The method is the same as above, by moving the Axios methods and attributes to the Request, i.e. iterating through the Axios instance to get the Interceptors object attached to the Request.

So just change the CreateAxiosFn method.

function CreateAxiosFn() {
  let axios = new Axios();
  
  let req = axios.request.bind(axios);
  // Add methods to the axios request method so that it has get,post... methodsUtils. Extend (req, Axios. Prototype, Axios)return req;
}
Copy the code

Now that the Request has an Interceptors object, when does the request get the callback function that the handler saved from the interceptors object?

Handlers and Handlers are the handlers that send the request. We execute the request we send, and then we get the Response interceptor’s handlers method to execute it.

Therefore, we need to modify the request method we wrote earlier

That was the case before.

request(config) {
    return new Promise(resolve= > {
        const {url = ' ', method = 'get', data = {}} = config;
        // Send an Ajax request
        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); })}Copy the code

But now the Request does not just send the Ajax request, but also the callback function in the interceptor handlers. Therefore, it is best to encapsulate the request to perform Ajax as a method

request(config) {
    this.sendAjax(config)
}
sendAjax(config){
    return new Promise(resolve= > {
        const {url = ' ', method = 'get', data = {}} = config;
        // Send an Ajax request
        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); })}Copy the code

Okay, so now we’re going to get the callback in handlers

request(config) {
    // Interceptor and request assembly queue
    let chain = [this.sendAjax.bind(this), undefined] // In pairs, the failure callback is not processed for now

    // 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)
    })

    // Execute the queue, one pair at a time, and assign the promise the latest value
    let promise = Promise.resolve(config);
    while(chain.length > 0) {
        promise = promise.then(chain.shift(), chain.shift())
    }
    return promise;
}

Copy the code

Let’s see if the interceptor works first, and then explain the code

<script type="text/javascript" src="./myAxios.js"></script>

<body>
<button class="btn">Click me to send request</button>
<script>
    // Add request interceptor
    axios.interceptors.request.use(function (config) {
        // What to do before sending the request
        config.method = "get";
        console.log("Intercepted by my request blocker, haha :",config);
        return config;
    }, function (error) {
        // What to do about the request error
        return Promise.reject(error);
    });

    // Add a response interceptor
    axios.interceptors.response.use(function (response) {
        // What to do with the response data
        console.log("Intercepted by my response blocker, ha ha.");
        response = {message:"I've replaced the response data. Aha, ha, ha."}
        return response;
    }, function (error) {
        // Do something about the response error
        console.log("Is that wrong?");
        return Promise.reject(error);
    });
    document.querySelector('.btn').onclick = function() {
        // Use the following method calls respectively to see the effect of myaxios
        axios({
          url: 'https://zhengzemin.cn/dist/static/fonts/element-icons.535877f.woff'
        }).then(res= > {
          console.log('response', res); })}</script>
</body>
Copy the code

Intercept successful!!

The following code understanding

We put the sendAjax request and undefined in the chain array first, and then put the paired callback of the request interceptors’ Handlers in the chain array header. The handlers’ acceptance callback is called to the end of the chain array.

It then incrementally fetches the pair-wise callbacks of the chain array.

promise = promise.then(chain.shift(), chain.shift())
Copy the code

This sentence essentially passes config from one promise to the next, possibly calling back to make some changes. What do you mean? Let’s use an example to illustrate this

First, this is how interceptors are used

// Add request interceptor
axios.interceptors.request.use(function (config) {
    // What to do before sending the request
    return config;
  }, function (error) {
    // What to do about the request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // What to do with the response data
    return response;
  }, function (error) {
    // Do something about the response error
    return Promise.reject(error);
  });
Copy the code

And then when you execute the request. The data for the chain array looks like this

chain = [
  function (config) {
    // What to do before sending the request
    return config;
  }, 
  
  function (error) {
    // What to do about the request error
    return Promise.reject(error);
  }
  this.sendAjax.bind(this), 
  
  undefined.function (response) {
    // What to do with the response data
    return response;
  }, 
  function (error) {
    // Do something about the response error
    return Promise.reject(error); }]Copy the code

The first promise.then(chain.shift(), chain.shift()), i.e

promise.then(
  function (config) {
    // What to do before sending the request
    return config;
  }, 
  
  function (error) {
    // What to do about the request error
    return Promise.reject(error); })Copy the code

Typically, a promise is in the Resolved state and is the result of a successful callback, which is executed

function (config) {
    // What to do before sending the request
    return config;
  }, 
Copy the code

Promise. then returns a new promise object.

For the sake of distinction, I’ll call this new promise object the first new promise object

This first new promise object is going to take

function (config) {
    // What to do before sending the request
    return config;
  }, 
Copy the code

The result of the resolve function is passed to the resolve function

resolve(config)
Copy the code

Make the state of the first new promise object returned resovLED and the data of the first new Promise object config.

Next, execute

promise.then(
  sendAjax(config)
  ,
  undefined
)
Copy the code

Note: The promise here is the first new promise object mentioned above.

The promise.then implementation of this returns a second new Promise object.

Because the promise in promise.then the state of the first new promise object is resolved, sendAjax() will be executed. And the first new Promise object’s data is fetched as config and passed into sendAjax().

When sendAjax is finished, a response is returned. The response is stored in the data of the second new Promise object.

Next, execute

promise.then(
  function (response) {
    // What to do with the response data
    return response;
  }, 
  function (error) {
    // Do something about the response error
    return Promise.reject(error); })Copy the code

Again, the second new Promise object’s data is taken out and passed in as the response argument

function (response) {
    // What to do with the response data
    return response;
  }, 
Copy the code

After the meal, a Promise object is returned. The data of the Promise object holds the result of the function execution, that is, the return value response.

And then return promise;

Return the promise. Yi? How to retrieve promise’s data. Let’s see how do we normally get response data

axios.get('https://zhengzemin.cn/dist/static/fonts/element-icons.535877f.woff')
    .then(res= > {
         console.log('getAxios responded successfully ', res);
    })
Copy the code

Receive response data in THEN. So the principle is the same as above, using the returned Promise’s data as the RES parameter.

Finally, present the complete myAxios code for a complete overview

MyAxios core code

Call the myAxios code (searchable initHandleWrite, in a handwritten Ajax test of the initHandleWrite method)

Axios compatibility handling

Axios is not supported on phones running Android 4.3 or below

The project found that axios was not supported on android 4.3 and below, mainly because promise was not available. Add the following polyfill.

Install ES6 – Promise in the project

cnpm install es6-promise --save-dev
Copy the code

Add at the beginning of axios.min.js

require('es6-promise').polyfill();
Copy the code

Compatibility issues in Internet Explorer

Axios supports IE8 +, but since it is based on Promise, when the project uses new knowledge such as ES6 syntax, there will be incompatible WITH IE. When you check the console, you will find that “Promise” is not defined and so on.

The solution

Install Babel-Polyfill in your project

npm install --save babel-polyfill
Copy the code

Import “babel-polyfill” in main.js

Modify the configuration file by adding the code to the webpack.base.conf.js filerequire("babel-polyfill")

After the preceding steps are complete, restart the project, and the data can be obtained normally in Internet Explorer

Axios cancels the previous request

Usage Scenarios:

1. Repeat click (we only want to request once) 2. Pagination (we quickly click the third page again, quickly click the fourth page, it is possible that we request the data of the fourth page back, the data of the third page back, so that the data of the third page is displayed, but we click the data of the fourth page) 3. When writing the map, there is a timeline, there is a lot of time, crazy click time, it is difficult to say which time is requested back first, so we have to cancel the last request, only request the last request 4.

I’m sure you already know the scenario of canceling the interface request

Axios officially provides a way to cancel the interface request

Axios provides a CancelToken function. This is a constructor that cancels the interface request. How to use this function

data(){
  return {
    cancel: this.axios.CancelToken.source()
  }
},
methods: {getData(){
    this.cancel.cancel();  // Cancel the previous request
    this.cancel = this.axios.CancelToken.source();  // Re-add the request
    
    this.axios.get(url,{
      params: query,
      cancelToken: this.cancel.token
    }).then(res= > console.log(res))
    .catch(err= > {
      // If the request is cancelled manually, then is not followed; catch is followed
      // If is entered only when we manually cancel the request
      if(err.toString() == 'Cancel'){}})}}Copy the code

Such benefits:

  • No matter how late the network is when I hit TAB, I dare to block your request and make sure my next request is not affected
  • Clicking on a page at a very high speed won’t make any difference
  • During development, we will encounter a problem of repeated clicks. Clicking the same button to send requests for many times in a short time will increase the burden on the server and consume the performance of the browser. Most of the time, we need to do an operation to cancel repeated clicks
  • How do you click on the timeline on the map, just cancel the last request, just request the last request

The original link

Juejin. Cn/post / 686374…

Reference documentation

Writing axios core principles by hand, I am no longer afraid of being asked by an interviewer about axios principles

Axios compatibility handling

Use AXIOS in VUE project to solve axiOS compatibility issues under IE

Axios cancels the interface request

Axios website