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 相关文章推荐
JavaScript 页面坐标相关知识整理
Jan 09 Javascript
Node.js配合node-http-proxy解决本地开发ajax跨域问题
Aug 31 Javascript
AngularJS之ionic 框架下实现 Localstorage本地存储
Apr 22 Javascript
vue实现动态数据绑定
Apr 28 Javascript
vue+element+Java实现批量删除功能
Apr 08 Javascript
JavaScript怎样在删除前添加确认弹出框?
May 27 Javascript
详解nvm管理多版本node踩坑
Jul 26 Javascript
使用layer弹窗提交表单时判断表单是否输入为空的例子
Sep 26 Javascript
JS页面动态绘图工具SVG,Canvas,VML介简介
Oct 16 Javascript
vant组件中 dialog的确认按钮的回调事件操作
Nov 04 Javascript
JavaScript构造函数原理及实现流程解析
Nov 19 Javascript
Vue Mint UI mt-swipe的使用方式
Jun 05 Vue.js
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 生成自动创建文件夹并上传文件的示例代码
2014/03/07 PHP
php显示指定目录下子目录的方法
2015/03/20 PHP
PHP小偷程序的设计与实现方法详解
2016/10/15 PHP
PHP实现的文件操作类及文件下载功能示例
2016/12/24 PHP
php+Memcached实现简单留言板功能示例
2017/02/15 PHP
PHP多进程之pcntl_fork的实例详解
2017/10/15 PHP
利用腾讯的ip地址库做ip物理地址定位
2010/07/24 Javascript
JQuery中html()方法使用不当带来的陷阱
2011/04/07 Javascript
基于Bootstrap的后台管理面板 Bootstrap Metro Dashboard
2016/06/17 Javascript
jQuery获取多种input值的简单实现方法
2016/06/20 Javascript
jquery插件treegrid树状表格的使用方法详解(.Net平台)
2017/01/03 Javascript
Extjs表单输入框异步校验的插件实现方法
2017/03/20 Javascript
vue页面切换到滚动页面显示顶部的实例
2018/03/13 Javascript
5分钟快速掌握JS中var、let和const的异同
2018/09/19 Javascript
使用Node.js写一个代码生成器的方法步骤
2019/05/10 Javascript
原生js代码能实现call和bind吗
2019/07/31 Javascript
vue中路由跳转不计入history的操作
2020/09/21 Javascript
[58:54]EG vs RNG 2019国际邀请赛小组赛 BO2 第一场 8.16
2019/08/18 DOTA
[22:07]DOTA2-DPC中国联赛 正赛 iG vs Magma 选手采访
2021/03/11 DOTA
pycharm 使用心得(五)断点调试
2014/06/06 Python
python爬虫系列Selenium定向爬取虎扑篮球图片详解
2017/11/15 Python
Python中return函数返回值实例用法
2020/11/19 Python
Html5监听手机摇一摇事件的实现
2019/11/07 HTML / CSS
美国网上眼镜商城:Zenni Optical
2016/11/20 全球购物
护理自荐信
2013/10/22 职场文书
本科毕业生自我鉴定
2013/11/02 职场文书
品牌宣传方案
2014/03/21 职场文书
个人委托书范本汇总
2014/10/01 职场文书
房地产工程部经理岗位职责
2015/04/09 职场文书
教师节大会主持词
2015/07/06 职场文书
用Python制作灯光秀短视频的思路详解
2021/04/13 Python
pytorch锁死在dataloader(训练时卡死)
2021/05/28 Python
css中z-index: 0和z-index: auto的区别
2021/08/23 HTML / CSS
mapstruct的用法之qualifiedByName示例详解
2022/04/06 Java/Android
python中 Flask Web 表单的使用方法
2022/05/20 Python
使用 CSS 构建强大且酷炫的粒子动画效果
2022/08/14 HTML / CSS