最近,在和朋友們協做的時候,暴露了許多問題,這其中最讓人頭疼的應該是,代碼組織問題。javascript
代碼組織能力會隨着知識面的拓展逐漸養成,可是在這個過程當中,你是否看過優秀的、讓人眼前一亮的代碼,並從中得到感悟,很大程度上影響着最終代碼組織能力的強弱。這並不僅僅看你的閱歷,和作過項目的多少,而是有意識地,主動地經過閱讀優秀的源碼,從別人那裏學來的一種軟實力。html
但是我在網上並無找到不少相似的文章,因而決定本身寫一篇,基於最近在作的競賽管理系統項目,梳理一下Vuex在項目中運用,總結出一些通用的東西。我不敢說本身的方式是最棒的,但相信能對你們有所幫助。前端
轉載請附上原文連接,支持原創vue
名稱: 競賽管理系統java
功能簡介:用戶信息增刪改查、賽事信息增刪改查、參賽記錄增刪改查、附件上傳、成績審覈等。ios
技術棧(前端):Vue全家桶 + ant-design-vuegit
項目地址:GitHubgithub
Vuex 並不限制你的代碼結構。可是,它規定了一些須要遵照的規則:vuex
- 應用層級的狀態應該集中到單個 store 對象中。
- 提交 mutation 是更改狀態的惟一方法,而且這個過程是同步的。
- 異步邏輯都應該封裝到 action 裏面。
只要你遵照以上規則,如何組織代碼隨你便。若是你的 store 文件太大,只需將 action、mutation 和 getter 分割到單獨的文件。axios
—— 摘自官方文檔
所謂應用層級的狀態,在本項目中即爲:用戶信息,賽事信息,參賽記錄。
將狀態做爲應用級的狀態的標準,能夠考慮該狀態是否爲全部組件或多個組件共用,或者你已經爲了讓這個狀態在組件間通訊耗費了巨大的精力。若是是這樣,就大膽地應用吧。
下面從Store提及。
做爲管理系統,少不了增刪改查。以管理員爲例,管理員的任務是對用戶信息(users)、賽事信息(races)、參賽記錄(records)進行增刪改查。使用Vuex咱們首先須要建立一個store目錄。
── store
├── index.js # 咱們組裝模塊並導出 store 的地方
├── actions.js # 根級別的 action
├── mutations.js # 根級別的 mutation
├── races # races 模塊
│ ├── index.js # 導出module
│ ├── mutations.js # module 級別的mutations
│ └── actions.js # module 級別的actions
├── users
│ ├── index.js
│ ├── mutations.js
│ └── actions.js
└── records
├── index.js
├── mutations.js
└── actions.js
複製代碼
因爲每一種數據都涉及增刪改查操做,我選擇使用Vuex中的Module來組織他們。每一個文件夾導出一個module,最終彙總到外部的index中,進而導出store對象,註冊Vuex。
每個模塊都有一個action和一個mutation,action用來發送請求,mutation根據請求結果修改state(因爲state結構簡單,我直接寫在了模塊內部),最終將變化體現到視圖上。
下面以賽事信息數據(races)示例:
如下三段代碼對應races目錄下:races/index.js,races/actions.js,races/mutations.js
/** * index.js */
import actions from './actions'
import mutations from './mutations'
// 這裏處處races模塊,並在store/index.js中註冊爲module
export default {
namespaced: true, // 設置命名空間只是爲了使模塊更加獨立,具體能夠參考官方文檔
state: {
races: []
},
mutations,
actions
}
複製代碼
/** * actions.js */
import { SET_RACE_LIST, ADD_RACE, UPDATE_RACE, DELETE_RACE } from '../mutation-types'
import { getRaceList, addRace, updateRace, deleteRace } from '../../api'
import { message } from 'ant-design-vue'
export default {
[SET_RACE_LIST] ({ commit }, params) {
return new Promise((resolve, reject) => {
getRaceList(params).then(({ data: races }) => {
resolve(races)
commit(SET_RACE_LIST, races)
}).catch(e => {
reject(e)
message.error('系統錯誤,請重試')
})
})
},
[ADD_RACE] ({ commit }, race) {
const stopLoading = message.loading('請稍後')
return new Promise((resolve, reject) => {
addRace(race).then(({ data }) => {
resolve(data)
commit(ADD_RACE, data)
message.success('添加成功')
}).catch(e => {
reject(e)
message.error('系統錯誤,請重試')
}).finally(() => {
stopLoading()
})
})
},
[UPDATE_RACE] ({ commit }, race) {
...
},
[DELETE_RACE] ({ commit }, _id) {
...
}
}
複製代碼
/** * mutations.js */
import { SET_RACE_LIST, ADD_RACE, UPDATE_RACE, DELETE_RACE } from '../mutation-types'
export default {
[SET_RACE_LIST] (state, races) {
state.races = races
},
[ADD_RACE] (state, race) {
state.races.push(race)
},
[UPDATE_RACE] (state, race) {
state.races = state.races.map(item => {
return item._id === race._id ? race : item
})
},
[DELETE_RACE] (state, _id) {
state.races = state.races.filter(item => item._id !== _id)
}
}
複製代碼
看了上面這些代碼,可能你會有如下疑問
/** * mutation-types.js —— 主要是定義一些常量 */
export const LOGIN = 'LOGIN'
export const LOGOUT = 'LOGOUT'
export const REFRESH_TOKEN = 'REFRESH_TOKEN'
// races 模塊
export const SET_RACE_LIST = 'SET_RACE_LIST'
export const ADD_RACE = 'ADD_RACE'
export const UPDATE_RACE = 'UPDATE_RACE'
export const DELETE_RACE = 'DELETE_RACE'
...
...
複製代碼
mutation-types定義的其實就是action和mutation的方法名,在這裏定義,再引過去,使用【中括號語法】定義方法,能夠保證同一類操做的名字不會寫錯,減小所以產生的bug,同時還能享受編譯器的語法提示,很香的。
實際上這些常量的值具體是什麼並不重要,只要不重複就行,通常會和變量名相同。還可使用ES6中的Symbol來實現。
他們的目的主要有兩點:
可能你要問了,變量名和變量值是同樣的,爲何不直接使用字符串呢?舉例以下:
import { ADD_RECORD } from 'mutation-types'
store.dispatch(ADD_RECORD) // 若是寫錯,編譯器會報錯,直接發現錯誤,當場改正
store.dispatch('ADD_RECORD') // 編譯器不會報錯,若是寫錯,運行時才能發現
複製代碼
其實若是你直接使用字符串,即使發現了錯誤,你也不必定知道是在哪裏寫錯的,由於這樣會使得代碼中處處都是字符串,誰能保證都寫對了呢?並且官方也是推薦使用這種方式來維護日趨龐大的項目得,有圖有真相:
什麼?你只是個小demo?好的沒問題,下一個👌。
大型項目中,爲了便於後期的項目維護,屢次出現的常量必定要提取出來,單獨定義。小demo請自便。
Action 相似於 mutation,不一樣在於:
這是官方給出的定義。在官方的示例文檔中,咱們能夠看到不少種使用方式。其中組合使用Action的方式值得你們留意,Action返回一個Promise對象就能夠自由地和業務邏輯進行組合。如此一來,咱們在組件中就能夠自定義不少邏輯,如自定義loading狀態:
import { GET_RACE_LIST } from 'mutation-types'
export default {
mounted () {
this.loading = true
this.$store.dispatch(GET_RACE_LIST).finally(() => {
this.loading = false
})
}
}
複製代碼
在上面races/action.js
的代碼中,引入了ant-design-vue
中的message api
。根據請求結果不一樣作出不一樣的提示,成功反饋,錯誤處理等等。這樣一來,組件中只須要調用store.dispatch()
發出請求,並基於請求結果作一些和組件有關的事情便可,跟數據有關的事情由action配合mutation自動處理好。組件中只要關注組件自己的邏輯就能夠了。
在races/actions.js
中有這樣一行代碼
import { getRaceList, addRace, updateRace, deleteRace } from '../../api'
複製代碼
API是單獨封裝的,理由很簡單,一是爲了複用,二是爲了解決相似mutation-types
的常量引用問題。試想若是每一次發請求都要將請求地址完整的寫上,那麼請求地址若是變化了,你必定會瘋掉的,甚至逼到你使用全局搜索來進行替換,這無疑是低效的。
這是個人API目錄結構
api
├── index.js # 導出api
├── handle404.js # 配合axios.js處理響應攔截
└── axios.js # 導出axios對象,作一些全局配置,如請求攔截,響應攔截,baseUrl等
複製代碼
大體是這樣,具體因業務不一樣而異,下面看看index.js
的內容:
/** * index.js 定義接口 */
...
...
export const getRaceList = params => axios.get('/race/list', { params })
export const addRace = data => axios.post('/race/add', data)
export const updateRace = data => axios.put('/race/update', data)
export const deleteRace = _id => {
return axios.delete('/race/delete', { data: { _id } })
}
...
...
複製代碼
關於axios.js
和handle404.js
的內容,涉及全局攔截和token無狀態刷新,感興趣的朋友能夠看個人另外一篇文章 —— 登錄驗證明踐 —— Token & Refresh Token。
曾幾什麼時候,被問起項目經驗時,心底裏都有一個疑問:什麼是項目?
我想起了大一時的本身,那時候我以爲項目就是特別高大上的東西,由不少文件組成,彼此之間存在着複雜的關係,必定是不少人一塊兒協做才能搞得出來的。可如今我明白,所謂項目,無非是一個功能集合。分離出的文件最終都會合起來,實現一個完整的功能,而之因此分開,是爲了維護方便。從這點上說,項目不管大小,只要是一個完整的功能,均可以稱做項目,哪怕只有一個簡單的index.html
文件。
固然啦,本文針對的主要仍是大型項目中的代碼組織方式,小demo的話怎麼舒服怎麼來。畢竟代碼風格這種東西因人而異,咱們不能說誰的代碼寫的漂亮就必定好。總的來講,良好的代碼風格,有助與排查錯誤,有利於團隊協做,有利於修身養性,有助於...(跑偏了)總之好處不會少。
最後,但願能對你們有所幫助,轉載請附上原文連接