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


Posted in Javascript onNovember 04, 2019

项目中的所有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... 。

Javascript 相关文章推荐
js 获取浏览器高度和宽度值(多浏览器)
Sep 02 Javascript
jquery prop的使用介绍及与attr的区别
Dec 19 Javascript
浅谈 javascript 事件处理
Jan 04 Javascript
jQuery trigger()方法用法介绍
Jan 13 Javascript
动态加载jQuery的方法
Jun 16 Javascript
JavaScript+html5 canvas制作色彩斑斓的正方形效果
Jan 27 Javascript
jquery.cookie.js实现用户登录保存密码功能的方法
Apr 15 Javascript
jquery.validate表单验证插件使用方法解析
Nov 07 Javascript
vue-axios使用详解
May 10 Javascript
Angularjs中date过滤器失效的问题及解决方法
Jul 06 Javascript
vue自定义js图片碎片轮播图切换效果的实现代码
Apr 28 Javascript
JS判断数组是否包含某元素实现方法汇总
Jun 24 Javascript
JS+CSS实现随机点名(实例代码)
Nov 04 #Javascript
Vue.js页面中有多个input搜索框如何实现防抖操作
Nov 04 #Javascript
详解利用eventemitter2实现Vue组件通信
Nov 04 #Javascript
jQuery实现form表单基于ajax无刷新提交方法实例代码
Nov 04 #jQuery
JS立即执行的匿名函数用法分析
Nov 04 #Javascript
小程序如何自主实现拦截器的示例代码
Nov 04 #Javascript
uni-app微信小程序登录并使用vuex存储登录状态的思路详解
Nov 04 #Javascript
You might like
php获取汉字首字母的函数
2013/11/07 PHP
thinkphp Apache配置重启Apache1 restart 出错解决办法
2017/02/15 PHP
php简单计算权重的方法示例【适合抽奖类应用】
2019/06/10 PHP
js获取提交的字符串的字节数
2009/02/09 Javascript
javascript void(0)的妙用
2009/10/21 Javascript
Jvascript学习实践案例(开发常用)
2012/06/25 Javascript
关于IE BUG与字符串截取substr的解决办法
2013/04/10 Javascript
node.js中的fs.readlinkSync方法使用说明
2014/12/17 Javascript
js实现文本框只允许输入数字并限制数字大小的方法
2015/08/19 Javascript
JS遍历对象属性的方法示例
2017/01/10 Javascript
bootstrap选项卡扩展功能详解
2017/06/14 Javascript
微信、QQ、微博、Safari中使用js唤起App
2018/01/24 Javascript
JS文件中加载jquery.js的实例代码
2018/05/05 jQuery
VUE 解决mode为history页面为空白的问题
2019/11/01 Javascript
vue搜索页开发实例代码详解(热门搜索,历史搜索,淘宝接口演示)
2020/04/11 Javascript
python中enumerate函数遍历元素用法分析
2016/03/11 Python
python调用fortran模块
2016/04/08 Python
Python进行数据提取的方法总结
2016/08/22 Python
CentOS6.5设置Django开发环境
2016/10/13 Python
对Tensorflow中的变量初始化函数详解
2018/07/27 Python
使用pyqt 实现重复打开多个相同界面
2019/12/13 Python
python装饰器代替set get方法实例
2019/12/19 Python
Python select及selectors模块概念用法详解
2020/06/22 Python
全球领先的各类汽车配件零售商:Advance Auto Parts
2016/08/26 全球购物
美国网上鞋城:Shoeline.com
2016/11/17 全球购物
英国著名的美容护肤和护发产品购物网站:Lookfantastic
2020/11/23 全球购物
教师业务学习制度
2014/01/25 职场文书
考试违纪检讨书
2014/02/02 职场文书
生日主持词
2014/03/20 职场文书
一般党员对照检查材料
2014/09/24 职场文书
80后婚前协议书范本
2014/10/24 职场文书
导游词开场白
2015/01/31 职场文书
小学英语教学经验交流材料
2015/11/02 职场文书
Flask使用SQLAlchemy实现持久化数据
2021/07/16 Python
集英社今正式宣布 成立游戏公司“集英社Games”
2022/03/31 其他游戏
MySQL性能指标TPS+QPS+IOPS压测
2022/08/05 MySQL