preface

Axios is a lightweight HTTP client that executes HTTP requests based on the XMLHttpRequest service, supports rich configurations, promises, browser-side and Node.js side. Since Vue2.0, Vue Has announced that it has removed its official recommendation for Vue-Resource and recommended Axios instead. Now Axios has become the first choice for most Vue developers. (If you’re not familiar with Axios, check out its API here).

The AXIos API is so friendly that you can easily use it directly in your project. But as your project gets bigger, you have to write all these things in place every time you make an HTTP request, such as setting the timeout, setting the request header, determining which address to use for the request, and handling errors. This duplication of effort is not only a waste of time, but also makes the code redundant and difficult to maintain.

To improve the quality of our code, we should re-encapsulate AXIos in our project.

So how do you encapsulate Axios?

The original

Before wrapping, let’s take a look at what an AXIos request would look like in a real project without wrapping. Something like this:

axios('http://localhost:3000/data', {
  method: 'GET',
  timeout: 1000,
  withCredentials: true,
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'xxx',
  },
  transformRequest: [function (data, headers) {
    returndata; }, // Other requests to configure... }).then((data) => {// todo: real business logic code console.log(data); }, (err) => {if (err.response.status === 401) {
  // handle authorization error
  }
  if(err.response.status === 403) {// Handle server forbidden error} // Other error handling..... console.log(err); });Copy the code

As you can see in this code, the page code logic is only at line 15, with a chunk of request configuration code at the top and a chunk of response error handling code at the bottom, which has little to do with page functionality, and is similar or even identical in some cases for each request. Imagine doing this for every request, a dozen requests, what would happen?

Encapsulate steps

The essence of encapsulation is to add a variety of things to the content to be encapsulated, and then present them as a new whole to the user for extensibility and ease of use.

What encapsulating Axios does is preconfigure the configuration shared by all HTTP requests in Axios, reserve the necessary parameters and interfaces, and return it as the new Axios.

Next, we implement an axiOS wrapper with good extensibility with a demo.

The demo directory structure is as follows (generated by vue-CLI 3.0) :

|--public/
|--mock/
|   |--db.json  # My new interface simulates data
|--src/
|   |--assets/
|   |--components/
|   |--router/
|   |--store/
|   |--views/
|       |--Home.Vue
|   |--App.vue
|   |--main.js
|   |--theme.styl
|--package.json
|...
Copy the code

Encapsulation target

I want to make an AXIos request from the Home page as simple as calling a method with only a few arguments, so I can focus on the business code.

1. Encapsulate AXIos into a separate file

  • Create the utils/http.js file under SRC
cd src
mkdir utils
touch http.js
Copy the code
  • The introduction of axios
// src/utils/http.js

import axios from 'axios';
Copy the code
  • You can also create a class and wrap it in a function, I just think classes are more semantic.
//src/utils/http.js

//...
class NewAxios {

}
Copy the code
  • Configure different request address bases for different environmentsprocess.env.NODE_ENVConfigured differentlybaseURL, allowing the project to automatically switch request host addresses in different environments simply by executing the corresponding packaging command.
// src/utils/http.js

//...
const getBaseUrl = (env) => {
  let base = {
    production: '/',
    development: 'http://localhost:3000'.test: 'http://localhost:3001',
  }[env];
  if(! base) { base ='/';
  }
  return base;
};

class NewAxios {
  constructor() { this.baseURL = getBaseUrl(process.env.NODE_ENV); }}Copy the code
  • Configure the timeout attribute. I usually set it to 10 seconds.
// src/utils/http.js

//...
class NewAxios {
  constructor() {/ /... this.timeout = 10000; }}Copy the code
  • Set the allowed Credentials attribute widthCredentials to true.
// src/utils/http.js

//...
class NewAxios {
  constructor() {
    //...
    this.withCredentials = true; }}Copy the code
  • Create an instance of this class with the method request onrequestMethod, create a new Axios instance, receive the request configuration parameters, process the parameters, add the configuration, and return the result of the axiOS instance’s request (a Promise object). You can also use the default exported AxiOS instance without creating it and put all the configuration on it, but then the entire project will share one AxiOS instance. Although this is fine for most projects, some projects may have completely different request and response structures for different service addresses, which may not be supported by a single instance. So to make encapsulation more general and flexible, I’ll use the CREATE method of Axios so that every request is a new Axios instance.
// src/utils/http.js //... class NewAxios { //... Request (options) {// Create a new Axios instance on each request. const instance = axios.create(); Const config = {// Merge the parameters passed by the user with the common configuration. . options, baseURL: this.baseURL, timeout: this.timeout, withCredentials: this.withCredentials, }; // Configure interceptors. You can configure interceptors based on urls. this.setInterceptors(instance, options.url);returninstance(config); // Return the execution result of the Axios instance}}Copy the code

Because the interceptor configuration has a lot of content, it is encapsulated as an internal function.

  • All changes to the request parameters made by the configuration request interceptor before sending the request are configured here. For example, add token credentials, set language, set content type, specify data format, and so on. Remember to return this configuration when you’re done, otherwise the entire request will not proceed. I’m going to configure a token here.
// src/utils/http.js //... class NewAxios { //... // The url here allows you to set different interceptors for the interface path that requires special handling.setInterceptors = (instance, Url) = > {instance. Interceptors. Request. Use ((config) = > {/ / request / / interceptor configuration token config. The headers. AuthorizationToken =localStorage.getItem('AuthorizationToken') | |' ';
      returnconfig; }, err => Promise.reject(err)); } / /... }Copy the code
  • Configure the response interceptor at the requestthenorcatchA round of pre-processing of the response data is performed before processing. For example, filtering response data, and more importantly, uniform error handling for various response error codes, as well as network disconnection processing and so on. I’m just judging 403 and disconnection.
// src/utils/http.js

//...
class NewAxios {
  //...
  setInterceptors = (instance, url) => { //... The instance. The interceptors. Response. Use ((response) = > {/ / response blocker / / todo: want to according to business needs, preliminary treatment, the result of the response in the console. Here the log ();return response;
    }, (err) => {
      if(err.response) {switch (err.response.status) {case '403':
            // todo: handler server forbidden error
            break;
            // todo: handler other status code
          default:
            break;
        }
        return Promise.reject(err.response);
      }
      if(! Window.navigator. Online) {// todo: jump to offline pagereturn- 1; }returnPromise.reject(err); }); } / /... }Copy the code

In addition, in interceptors, it is also suitable to place loading and other buffering effects: display loading in the request interceptor, and remove loading in the response interceptor. This allows all requests to have a uniform loading effect.

  • New instances are exported by default
// src/utils/http.js

//...
export default new NewAxios();
Copy the code

The final complete code looks like this:

// src/utils/http.js

import axios from 'axios';

const getBaseUrl = (env) => {
  let base = {
    production: '/',
    development: 'http://localhost:3000'.test: 'http://localhost:3001',
  }[env];
  if(! base) { base ='/';
  }
  return base;
};

class NewAxios {
  constructor() {
    this.baseURL = getBaseUrl(process.env.NODE_ENV);
    this.timeout = 10000;
    this.withCredentials = true;
  }

  setInterceptors = (instance, Url) = > {instance. Interceptors. Request. Use ((config) = > {/ / add loading/here/configuration token config. The headers. AuthorizationToken =localStorage.getItem('AuthorizationToken') | |' ';
      returnconfig; }, err => Promise.reject(err)); The instance. The interceptors. Response. Use ((response) = > {/ / remove loading / / todo: here think according to business needs, preliminary treatment, the result of the response are herereturn response;
    }, (err) => {
      if(err.response) {switch (err.response.status) {case '403':
            // todo: handler server forbidden error
            break;
            // todo: handler other status code
          default:
            break;
        }
        return Promise.reject(err.response);
      }
      if(! Window.navigator. Online) {// todo: jump to offline pagereturn- 1; }returnPromise.reject(err); }); } request(options) {// Each request creates a new Axios instance. const instance = axios.create(); Const config = {// Merge the parameters passed by the user with the common configuration. . options, baseURL: this.baseURL, timeout: this.timeout, withCredentials: this.withCredentials, }; // Configure interceptors. You can configure interceptors based on urls. this.setInterceptors(instance, options.url);returninstance(config); // Return the execution result of the Axios instance}}export default new NewAxios();
Copy the code

The Axios encapsulation is now 80% complete. We need to combine AXIOS with interfaces for one more layer of encapsulation to achieve the encapsulation goal I set at the beginning.

2. Encapsulate the API with the new AXIOS

  • Create one in the SRC directoryapiFolder. All interfaces involved in HTTP requests are centrally managed in this directory.
  • newhome.js. We need to classify interfaces according to certain rules. A class of interfaces corresponds to a JS file. This can be by page, by module, etc. So just to make this a little bit more intuitive, I’m just going to break it down into pages. The actual according to their own needs to decide.
  • Use the new Axios to encapsulate the API (fix the VALUE of the URL, merge the parameters passed in by the user), and then export the functions by naming them.
// src/api/home.js 

import axios from '@/utils/http';
exportconst fetchData = options => axios.request({ ... options, url:'/data'});export default {};
Copy the code
  • Create one in the API directoryindex.js, the other files of the interface are in this file summary export.
// src/api/index.js

export * from './home';
Copy the code

This layer of encapsulation encapsulates our new AXIos into cleaner and more semantic interface methods.

Our directory structure now looks like this:

|--public/
|--mock/
|   |--db.json  Interface simulation data
|--src/
|   |--api/     All interfaces are concentrated in this directory
|       |--home.js  The interface involved in the # Home page is encapsulated here
|       |--index.js # Entry for all interface calls in the project
|   |--assets/
|   |--components/
|   |--router/
|   |--store/
|   |--utils/
|       |--http.js  # Axios is encapsulated here
|   |--views/
|       |--Home.Vue
|   |--App.vue
|   |--main.js
|   |--theme.styl
|--package.json
|...
Copy the code

Use the encapsulated Axios

Now when we want to make an HTTP request, we just need to import the index.js file under the API to call any interface, and use the wrapped AXIos.

// src/views/Home.vue

<template>
  <div class="home">
    <h1>This is home page</h1>
  </div>
</template>

<script>
// @ is an alias to /src
import { fetchData } from '@/api/index';

export default {
  name: 'home'.mounted() {fetchData() // Axios request is here.then ((data) => {console.log(data); }) .catch((err) => { console.log(err); }); }}; </script>Copy the code

The AXIos request is wrapped in the fetchData function, and the page request doesn’t need any AXIOS API at all. It silently initiates the request and gets the response, just as if it were calling a simple Promise function. In addition, you can focus on the business function in the page without being distracted by other things.

run

Run NPM Run Serve to start the project and execute NPM Run Mock to start the service mock interface.

Now open localhost:8080 and you will see the home page. Open the browser console and you can see the printed response to the request:

Simple, elegant.

conclusion

  1. The idea of encapsulation is a useful one in front end technology, and it can be seen in simple AxiOS and interface encapsulation.
  2. encapsulationaxiosThere is no absolute standard, as long as your package can meet the needs of your project, and easy to use, that is a good package.
  3. BTW: The above package provides you with a encapsulated AXIOS and API framework, which has been encapsulated by the above processaxiosIt is not limited to Vue, React projects can also be used, it can be applied to any front-end project.

The code for this article is available here: github.com/yc111/wrap-…

Welcome to communicate ~

Welcome to reprint, reprint please indicate the source: champyin.com/2019/12/23/…