In fact, Axios already provides a lot of powerful apis, which we can call directly in the actual use, but each team project calls Axios in a different way, especially in a large team project, with different back-end interaction logic, complex configuration, many addresses. Therefore, a unified style, flexible configuration, centralized management of the request program is essential.

Step 1: Interface management

Start by creating a folder called API in your project to unify the logic for interacting with the background.

classification

Create separate folders under the API folder and sort the interface addresses by type (this is necessary, especially in large projects where sorting by type allows you to quickly locate where interfaces are written) :

Create it again under each folderindex.jsFile, writes all interface addresses belonging to this type:

cms

. , export const CMS_DATA = '/cms/renderData'Copy the code

member

. , export const MEMBER_INFO = '/rights/memberInfo'Copy the code

throw

Create index.js file in API folder:

Expose all types of interfaces uniformly:

/ / CMS information
export * from './cms'
// Membership information
export * from './member'
Copy the code

Step 2: Cache mechanism

Create cache.js file in API folder:

A set of caching mechanism is developed based on AXIos, which can cache each request result based on the request address URL and the request parameter params. Meanwhile, a cache deadline and a cache mode can be set for each request result:

export default class Cache {
    constructor(axios, config = {}) {
        this.axios = axios
        this.caches = []
        if (!this.axios) {
            throw new Error('Please pass in an axios instance')}this.config = config
        this.defaultConfig = {
            cache: false.expire: 100 * 1000
        }
        this.CancelToken = this.axios.CancelToken
        this.init()
    }

    init() {
        this.requestInterceptor(this.config.requestInterceptorFn)
        this.responseInterceptor(this.config.responseInterceptorFn)
        window.onbeforeunload = () = > {
            this.mapStorage()
        }
    }

    requestInterceptor(callback) {
        this.axios.interceptors.request.use(async config => {
            let newConfig = callback && (await callback(config))
            config = newConfig || config
            let { url, data, params, cacheMode, cache = this.defaultConfig.cache, expire = this.defaultConfig.expire } = config
            if (cache === true) {
                let getKey = data ? `${url}? cacheParams=${data}` : `${url}? cacheParams=${params}`
                let obj = this.getStorage(cacheMode, getKey)
                // Check whether the cached data exists
                if (obj) {
                    let curTime = this.getExpireTime()
                    let source = this.CancelToken.source()
                    config.cancelToken = source.token
                    // Check whether the cache data exists and if it does, whether it has expired. If not, stop the request and return to the cache
                    if (curTime - obj.expire < expire) {
                        source.cancel(obj)
                    } else {
                        this.removeStorage(cacheMode, url)
                    }
                }
            } else {
                this.clearStorage(url)
            }
            return config
        }, error= > {
            return Promise.reject(error)
        })
    }

    responseInterceptor(callback) {
        this.axios.interceptors.response.use(async response => {
            let newResponse = callback && (await callback(response))
            response = newResponse || response
            // the http request error, do not store the result, direct return result
            if(response.status ! = =200|| response.data.ret || ! response.data.success) {return response.data
            }
            /* * `data` is the data to be sent as the request body, only applicable for request methods 'PUT', 'POST', and 'PATCH' * `params` are the URL parameters to be sent with the request, can be applicable for request methods 'GET' */
            let { url, cache, cacheMode, data, params } = response.config
            if (cache === true) {
                let obj = {
                    expire: this.getExpireTime(),
                    params,
                    data,
                    result: response.data
                }
                let setKey = data ? `${url}? cacheParams=${data}` : `${url}? cacheParams=${params}`
                this.caches.push(setKey)
                this.setStorage(cacheMode, setKey, obj)
            }
            return response.data
        }, error= > {
            let newError = callback && (await callback(newError))
            error = newError || error
            // Return cached data
            if (this.axios.isCancel(error)) {
                return Promise.resolve(error.message.result)
            }
            return Promise.reject(error)
        })
    }

    // Set the cache
    setStorage(mode = 'sessionStorage', key, cache) {
        window[mode].setItem(key, JSON.stringify(cache))
    }

    // Get the cache
    getStorage(mode = 'sessionStorage', key) {
        let data = window[mode].getItem(key)
        return JSON.parse(data)
    }

    // Clear the cache
    removeStorage(mode = 'sessionStorage', key) {
        window[mode].removeItem(key)
    }

    // Set the expiration time
    getExpireTime() {
        return new Date().getTime()
    }

    // Clear the cache
    clearStorage(key) {
        if (window.localStorage.getItem(key)) {
            window.localStorage.removeItem(key)
        } else {
            window.sessionStorage.removeItem(key)
        }
    }

    // Clear the unused cache
    mapStorage() {
        let length = window.localStorage.length
        if (length) {
            for (let i = 0; i < length; i++) {
                let key = window.localStorage.key(i)
                if (!this.caches.includes(key) && key.includes('? cacheParams=')) {
                    window.localStorage.removeItem(key)
                }
            }
        }
    }
}

Copy the code

Since the caching mechanism is based on THE URL + Params to cache, the browser will directly read the cache within the validity period of the same URL + Params, and will not send a request. If the expiration date expires or the request address changes or the request parameters change, the browser bypasses the cache and sends the request directly. (Scenarios that support paging caching)

Step 3 configure Axios

inapiCreate a folderconfig.jsFiles, for storageaxiosSome pre-configuration information for:

Global configuration

import axios from 'axios'
import Cache from './cache'

axios.defaults.withCredentials = true
axios.defaults.baseURL = process.env.NODE_ENV === 'production' ? ' ' : '/api'
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'.Copy the code

The interceptor

Since the cache mechanism is designed to rely on the interceptor mechanism, in order to avoid additional interceptor configuration, the cache mechanism is designed with an interceptor configuration entry as follows:

new Cache(axios, {
    requestInterceptorFn: config= > {
        // Custom request interceptor
        / * * /
        // Return config with Promise
        return Promise.resolve(config)
    },
    responseInterceptorFn: response= > {
        // Custom response interceptor, can uniform return data format can also intercept errors
        / * * /
        // Return the response with a Promise
        return Promise.resolve(response)
    }
})

export default axios
Copy the code

If caching is not used, interceptors can be configured as follows:

axios.interceptors.request.use(config= > {
    // Do something before request is sent
    return config;
}, error= > {
    // Do something with request error
    return Promise.reject(error);
});

// Add a response interceptor
axios.interceptors.response.use(response= > {
    // Do something with response data
    return response || {};
}, error= > {
    // Do something with response error
    return Promise.reject(null);
});

export default axios
Copy the code

Step 4: Request encapsulation

Create base.js file in API folder:

There are several common methods that are encapsulated. Here are some common get and POST methods:

import axios from './config'
import qs from 'qs'

export const post = (url, data, extend = {isJson: true, cache: false}) = > {
    let defaultConfig = {
        url,
        method: 'POST'.data: extend.isJson ? data : qs.stringify(data) // use isJson to determine whether the parameter format isJson or formData. The default isJson
    }
    letconfig = {... defaultConfig, ... extend}return axios(config).then(res= > {
        // The format of the returned data can be unified
        return res
    }, err= > {
        return Promise.reject(err)
    })
}

export const get = (url, data, extend = {cache: false}) = > {
    let defaultConfig = {
        url,
        method: 'GET'.params: data
    }
    letconfig = {... defaultConfig, ... extend}return axios(config).then(res= > {
        // The format of the returned data can be unified
        return res
    }, err= > {
        return Promise.reject(err)
    })
}
Copy the code

Step 5: Global registration

Create the install.js file in the API folder:

Register the encapsulated method globally:

import { get, post } from 'api/base'

export const install = function(Vue, config = {}) {
    Vue.prototype.$_get = get
    Vue.prototype.$_post = post
}
Copy the code

In main.js, write:

import { install as Axios } from './api/install'
Vue.use(Axios)
Copy the code

Step 6: Call

The address of the interface you want to call is simply introduced:

import { CMS_DATA, MEMBER_INFO } from 'api'

methods: {
    receiveCMS() {
        // The post parameter takes the form formData
        this.$_post(CMS_DATA, data, { jsJson: false }).then(res= > {
            console.log(res)
        }),
    },
    receiveMember() {
        // Enable the cache, set the cache duration to one hour, and set the cache mode to localStorage
        this.$_get(MEMBER_INFO, data, { cache: true.expires: 1000 * 60 * 60.cacheMode: 'localStorage' }).then(res= > {
            console.log(res)
        }),
    }
}
Copy the code

The cache is disabled by default. You need to manually enable it. If enabled, the cache validity period is 10 minutes by default and the cache mode is sessionStorage by default.

The last

The whole design is complete:

Of course, with the complexity of the project, there are still many aspects that can be optimized in this scheme, such as global loading. As I feel that it is suitable for mobile terminal but not PC terminal, I will not give an example here. Students who need it can package it in the fourth step. Global configuration, for example, can be supplemented in step 3.

It’s not the best plan, but it’s the most elegant way I’ve come up with it so far, and I welcome your valuable comments.