Organization and return data processing of API in Vue project

Time:2019-11-14

All the API configurations in the project are placed in one file, which is easy to find and modify. The API version is read from the configuration file (config. JS), which usesapiPrefix + urlForm composition.

In the configuration file, API is configured withHTTP request method URLBy defaultGETIt can not be written. The request mode is in uppercase and the dynamic parameters are in uppercasePlaceholderThe form.

// services/api.js
export default {
  login: 'POST /login',
  logout: '/logout',
  queryUser: '/user/:id'
}

Then you need a method to parse the API configuration above

// services/index.js
import request from '../utils/request'
import api from './api'

const gen = params => {
  let url = params
  let method = 'GET'
  const paramsArr = params.split(' ')
  if (paramsArr.length === 2) {
    method = paramsArr[0]
    url = paramsArr[1]
  }

  return data => {
    return request({
      url,
      data,
      method
    })
  }
}

const apiFunc = {}
for (const key in api) {
  apiFunc[key] = gen(api[key])
}

export default apiFunc

In the above coderequestEncapsulationaxiosThe code is as follows:

// utils/request.js
import axios from 'axios'
import store from '../store'
import { apiPrefix } from './config'
import { Message, MessageBox } from 'element-ui'

let cancel
const CancelToken = axios.CancelToken
const service = axios.create({
  baseURL: apiPrefix,
  timeout: 3000,
  cancelToken: new CancelToken(c => cancel = c)
})

service.interceptors.response.use(
  response => {
    const resType = response.config.responseType
    const res = response.data
    //Binary
    if (resType && resType !== 'json') {
      const filename = response.headers['content-disposition'].split('filename=')[1]
      return {
        filename,
        blob: res
      }
    }
    if (res.code !== 200) {
      //Login failure
      if (res.code === 401) {
        let timer = null
        //Cancel subsequent requests
        cancel(res.msg)
        //Update store and localstorage status
        store.commit('user/RESET_LOGIN_STATE', false)
        MessageBox. Confirm ('login is invalid, please login again ',' prompt '{
          Confirmbuttontext: 'OK',
          showClose: false,
          showCancelButton: false,
          type: 'warning'
        }).then(() => {
          clearTimeout(timer)
          reload()
        })
        timer = setTimeout(reload, 1000)
      }
      Const errmsg = res.msg | 'server returned error'
      popupMsg(errMsg)
      return Promise.reject(errMsg)
    }
    return res
  },
  error => {
    // timeout
    if (error.message.includes('timeout')) {
      Const errmsg = 'network request timeout, please try again later'
      popupMsg(errMsg)
      cancel(errMsg)
    }
  }
)

function reload() {
  window.location.href = `${window.location.origin}/#/login`
  window.location.reload()
}

function popupMsg(msg, sec = 5000) {
  Message({
    message: msg,
    type: 'error',
    duration: sec
  })
}

export default service

In the process of encapsulationNetwork request timeoutWhen downloading table data, the back end directly returns the binary fileCancel subsequent interface request after login failure

In the development of background management system project, vuex is basically used. In the actual development process, it is usually divided by functionmoduleInxx.vuePassed directly in the documentmapActionsTo inject methods with side effects.

// store/common.js
import service from '../services'

const actions = {
  async login(data) {
    const res = await service.login(data)
    return Promise.resolve(res)
  }
}

export default {
  namespaced: true,
  state: {},
  getters: {},
  mutations: {},
  actions
}

Actually, it’s up thereapiFuncMethod can be called directly to return results. Why is this unnecessary here? Because sometimes the parameters of an interface need to be processed. There is obvious code redundancy in each page, which is inconvenient to modify. At the same time, there will be different interfaces to get the same data, but different back-end pages give different interfaces. Here, it can be made clear which interface is needed according to the parameters.

PackagedactionIn some cases, you do not need to return the data (promise), because you can put all the data states of the whole page in the state, and directly pass thecommitOnemutationTo modifystate, directly pass all numbers in the pagemapStateOr directlythis.$store.state.xxTo visit. If you want to bind state in state tov-modelYes, you can.computedDefinition ingetandsetFor example:

export default {
  computed: {
    dateType: {
      get() {
        return this.$store.state.device.dateType
      },
      set(val) {
        //Some processing, such as dynamically changing the configuration of 'El datep icker' according to the day, week and month
      }
    }
  }
}

In a project, it is not recommended to process all the states in the page in vuex, except for some global states and some components with linkage effect. Because in the current route switch to other routes, in vuexstateThe data of is not reset. If you cut it back, you will find that the original data has not changed. And once definedstateWhen there are too many nesting, it is very troublesome to reset.

There is another problem in usingechartsWhen you dynamically change the chart drawing based on some filters, you will find thatmapStateandgettersYou can’t get the latest data in, you have to write a long string ofthis.$store.state.moduleA.moduleB.xxx.stateOf course, we can also use the convenient mapping method provided by vuexconst { mapState } = createNamespacedHelper('some/nested/module')But there is a problem once the same page referencesmoduleMany of them involve taking multiple aliases.

It is used in the project as follows:

import { mapActions } from 'vuex'
import apiFunc from '../services'

export default {
  methods: {
    ...mapActions('common', [
      'login'
    ]),
    async onLogin() {
      const params = {}
      const res = await apiFunc.login(params)
    }
  }
}

Pay attention to useasync/awaitIf the error in the outer layer is not caught, it is better to add another layertry...catch...

LastThe above is a little personal experience sharing, part of which refers to the idea of antd admin.