項目中的全部Api配置放在一個文件中,便於查找和修改,Api的版本從配置文件(config.js)中讀取,採用apiPrefix + url
的形式組成。javascript
在配置文件中,Api 的配置採用Http請求方式 url
的方式,默認狀況下GET
能夠不寫,請求方式統一採用大寫形式,動態參數採用: 佔位符
的形式。vue
// services/api.js export default { login: 'POST /login', logout: '/logout', queryUser: '/user/:id' }
而後須要一個方法在解析上面的Api配置java
// 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
後暴露出來的方法,代碼以下:ios
// 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
在封裝的過程當中處理了網絡請求超時、下載表數據時後端直接返回二進制文件的狀況、登陸失效後取消後續接口請求。git
在開發後臺管理系統項目時,基本都會用到Vuex。在實際的開發過程當中一般會按功能進行分module
,在xx.vue
文件中直接經過mapActions
來注入帶反作用的方法。github
// 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
方法中是能夠直接調用返回結果,爲何在這裏還畫蛇添足呢?是由於有些時候一個接口的參數須要加工處理,在每一個頁面處理明顯代碼冗餘,修改不方便,同時也會出現獲取一樣的數據可是不一樣的頁面後端給到的是不一樣的接口,這裏就能夠作到根據參數來明確須要哪一個接口。vuex
在封裝的action
中,有些時候是不須要返回數據(Promise),由於徹底能夠整個頁面的數據狀態所有放在state中,接收到數據後直接經過commit
一個mutation
來修改state
,在頁面中直接把全部的數經過mapState
或者直接this.$store.state.xx
來訪問。若是想要綁定state中的狀態到v-model
,能夠在computed
中定義get
和set
來實現,例如:element-ui
export default { computed: { dateType: { get() { return this.$store.state.device.dateType }, set(val) { // 一些處理,好比根據日、周、月來動態改變`el-datep-icker`的配置 } } } }
在項目中不建議把頁面中的全部狀態所有放在vuex中處理,除了一些全局狀態和一些組件之間具備聯動效應的。由於在當前路由切換到其它路由, vuex中state
的數據是沒有被重置的,再切回來會發現原有的數據並無變化等問題。並且一旦在定義state
時嵌套太多,重置就很麻煩了。json
還有一個問題在使用echarts時,根據一些篩選來動態改變圖表繪製時,會發現從mapState
和getters
中並不能獲取到最新的數據,還得手動寫一長串的this.$store.state.moduleA.moduleB.xxx.state
,固然咱們也可使用vuex提供的便捷映射方法const { mapState } = createNamespacedHelper('some/nested/module')
,可是有一個問題是一旦同一個頁面引用的module
不少,就涉及到取多個不一樣的別名問題了。axios
在項目中使用方式以下:
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的思路。