Vue项目中Api的组织和返回数据处理

更新日期: 2019-10-26阅读: 2.2k标签: 数据

项目中的所有api配置放在一个文件中,便于查找和修改,Api的版本从配置文件(config.js)中读取,采用apiPrefix + url的形式组成。

在配置文件中,Api 的配置采用Http请求方式 url的方式,默认情况下GET可以不写,请求方式统一采用大写形式,动态参数采用: 占位符的形式。

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

然后需要一个方法在解析上面的Api配置

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

上面代码中的request是封装axios后暴露出来的方法,代码如下:

// 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
    // 二进制文件
    if (resType && resType !== 'json') {
      const filename = response.headers['content-disposition'].split('filename=')[1]
      return {
        filename,
        blob: res
      }
    }
    if (res.code !== 200) {
      // 登录失效
      if (res.code === 401) {
        let timer = null
        // 取消后续请求
        cancel(res.msg)
        // 更新store及localStorage状态
        store.commit('user/RESET_LOGIN_STATE', false)
        MessageBox.confirm('登录已失效,请重新登录', '提示', {
          confirmButtonText: '确定',
          showClose: false,
          showCancelButton: false,
          type: 'warning'
        }).then(() => {
          clearTimeout(timer)
          reload()
        })
        timer = setTimeout(reload, 1000)
      }
      const errMsg = res.msg || '服务器返回错误'
      popupMsg(errMsg)
      return Promise.reject(errMsg)
    }
    return res
  },
  error => {
    // 超时
    if (error.message.includes('timeout')) {
      const errMsg = '网络请求超时, 请稍后重试'
      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

在封装的过程中处理了网络请求超时下载表数据时后端直接返回二进制文件的情况登录失效后取消后续接口请求

在开发后台管理系统项目时,基本都会用到vuex。在实际的开发过程中通常会按功能进行分module,在xx.vue文件中直接通过mapActions来注入带副作用的方法。

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

其实在上面的apiFunc方法中是可以直接调用返回结果,为什么在这里还多此一举呢?是因为有些时候一个接口的参数需要加工处理,在每个页面处理明显代码冗余,修改不方便,同时也会出现获取同样的数据但是不同的页面后端给到的是不同的接口,这里就可以做到根据参数来明确需要哪个接口。

在封装的action中,有些时候是不需要返回数据(Promise),因为完全可以整个页面的数据状态全部放在state中,接收到数据后直接通过commit一个mutation来修改state,在页面中直接把所有的数通过mapState或者直接this.$store.state.xx来访问。如果想要绑定state中的状态到v-model,可以在computed中定义get和set来实现,例如:

export default {
  computed: {
    dateType: {
      get() {
        return this.$store.state.device.dateType
      },
      set(val) {
        // 一些处理,比如根据日、周、月来动态改变`el-datep-icker`的配置
      }
    }
  }
}

在项目中不建议把页面中的所有状态全部放在vuex中处理,除了一些全局状态和一些组件之间具有联动效应的。因为在当前路由切换到其它路由, vuex中state的数据是没有被重置的,再切回来会发现原有的数据并没有变化等问题。而且一旦在定义state时嵌套太多,重置就很麻烦了。

还有一个问题在使用echarts时,根据一些筛选来动态改变图表绘制时,会发现从mapState和getters中并不能获取到最新的数据,还得手动写一长串的this.$store.state.moduleA.moduleB.xxx.state,当然我们也可以使用vuex提供的便捷映射方法const { mapState } = createNamespacedHelper('some/nested/module'),但是有一个问题是一旦同一个页面引用的module很多,就涉及到取多个不同的别名问题了。

在项目中使用方式如下:

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

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

注意在使用async/await时如果外层的错误没有捕捉到,最好加一层try...catch...。

最后:以上是个人的一点点小经验分享,部分参考了antd-admin的思路。

链接: https://www.fly63.com/article/detial/6197

双向数据绑定与单向数据绑定的各自优势和关系

在react中是单向数据绑定,而在vue和augular中的特色是双向数据绑定。为什么会选择两种不同的机制呢?我猜测是两种不同的机制有不同的适应场景,查了一些资料后,总结一下。

原生JS数据绑定的实现

双向数据绑定是非常重要的特性 —— 将JS模型与HTML视图对应,能减少模板编译时间同时提高用户体验。我们将学习在不使用框架的情况下,使用原生JS实现双向绑定 —— 一种为Object.observe

JavaScript判断数据类型的多种方法【 js判断一个变量的类型】

js判断数据类型的多种方法,主要包括:typeof、instanceof、 constructor、 prototype.toString.call()等,下面就逐一介绍它们的异同。

javascript中的typeof返回的数据类型_以及强制/隐式类型转换

由于js为弱类型语言拥有动态类型,这意味着相同的变量可用作不同的类型。 typeof 运算符返回一个用来表示表达式的数据类型的字符串,目前typeof返回的字符串有以下这些: undefined、boolean、string、number、object、function、“symbol

使用typeof obj===‘object’潜在的问题,并不能确定obj是否是一个对象?

在js中我们直接这样写typeof obj===‘object’有什么问题呢?发现Array, Object,null都被认为是一个对象了。如何解决这种情况,能保证判断obj是否为一个对象

js进制数之间以及和字符之间的转换

js要处理十六进制,十进制,字符之间的转换,发现有很多差不多且书写不正确的方法.一个一个实践才真正清楚如何转换,现在来记录一下它们之间转换的方法。

js判断数字是奇数还是偶数的2种方法实现

奇数和偶数的判断是数学运算中经常碰到的问题,这篇文章主要讲解通过JavaScript来实现奇偶数的判断。2种判断方法:求余% 、&1

js算法_判断数字是否为素数/质数

质数又称素数。指在一个大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数。比如100以内共25个,js实现代码如下。

Js数据类型转换_JavaScript 那些不经意间发生的数据类型自动转换

JavaScript自动类型转换真的非常常见,常用的一些便捷的转类型的方式,都是依靠自动转换产生的。比如 转数字 : + x 、 x - 0 , 转字符串 : \\\"\\\" + x 等等。现在总算知道为什么可以这样便捷转换。

Js中实现XML和String相互转化

XML是标准通用标记语言 (SGML) 的子集,非常适合 Web 传输。XML 提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据。 这篇文章主要介绍Js中实现XML和String相互转化

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!