Vuex架構指南 —— 你真的會用Vuex嗎?

前言

最近,在和朋友們協做的時候,暴露了許多問題,這其中最讓人頭疼的應該是,代碼組織問題javascript

代碼組織能力會隨着知識面的拓展逐漸養成,可是在這個過程當中,你是否看過優秀的、讓人眼前一亮的代碼,並從中得到感悟,很大程度上影響着最終代碼組織能力的強弱。這並不僅僅看你的閱歷,和作過項目的多少,而是有意識地,主動地經過閱讀優秀的源碼,從別人那裏學來的一種軟實力。html

但是我在網上並無找到不少相似的文章,因而決定本身寫一篇,基於最近在作的競賽管理系統項目,梳理一下Vuex在項目中運用,總結出一些通用的東西。我不敢說本身的方式是最棒的,但相信能對你們有所幫助。前端

轉載請附上原文連接,支持原創vue

項目簡介

名稱: 競賽管理系統java

功能簡介:用戶信息增刪改查、賽事信息增刪改查、參賽記錄增刪改查、附件上傳、成績審覈等。ios

技術棧(前端):Vue全家桶 + ant-design-vuegit

項目地址:GitHubgithub

項目組織 —— Vuex部分

Vuex 並不限制你的代碼結構。可是,它規定了一些須要遵照的規則:vuex

  1. 應用層級的狀態應該集中到單個 store 對象中。
  2. 提交 mutation 是更改狀態的惟一方法,而且這個過程是同步的。
  3. 異步邏輯都應該封裝到 action 裏面。

只要你遵照以上規則,如何組織代碼隨你便。若是你的 store 文件太大,只需將 action、mutation 和 getter 分割到單獨的文件。axios

—— 摘自官方文檔

所謂應用層級的狀態,在本項目中即爲:用戶信息,賽事信息,參賽記錄。

將狀態做爲應用級的狀態的標準,能夠考慮該狀態是否爲全部組件或多個組件共用,或者你已經爲了讓這個狀態在組件間通訊耗費了巨大的精力。若是是這樣,就大膽地應用吧。

下面從Store提及。

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目錄下: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是什麼?

/** * 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來實現。

他們的目的主要有兩點:

  1. 使得vuex的函數調用更具語義化
  2. 減小因爲名字寫錯引起的bug

可能你要問了,變量名和變量值是同樣的,爲何不直接使用字符串呢?舉例以下:

import { ADD_RECORD } from 'mutation-types'
store.dispatch(ADD_RECORD) // 若是寫錯,編譯器會報錯,直接發現錯誤,當場改正
store.dispatch('ADD_RECORD') // 編譯器不會報錯,若是寫錯,運行時才能發現
複製代碼

其實若是你直接使用字符串,即使發現了錯誤,你也不必定知道是在哪裏寫錯的,由於這樣會使得代碼中處處都是字符串,誰能保證都寫對了呢?並且官方也是推薦使用這種方式來維護日趨龐大的項目得,有圖有真相:

vuex官方推薦使用mutation-types的方式

什麼?你只是個小demo?好的沒問題,下一個👌。

大型項目中,爲了便於後期的項目維護,屢次出現的常量必定要提取出來,單獨定義。小demo請自便。

action寫什麼?

Action 相似於 mutation,不一樣在於:

  • Action 提交的是 mutation,而不是直接變動state。
  • Action 能夠包含任意異步操做

這是官方給出的定義。在官方的示例文檔中,咱們能夠看到不少種使用方式。其中組合使用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自動處理好。組件中只要關注組件自己的邏輯就能夠了。

API

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.jshandle404.js的內容,涉及全局攔截和token無狀態刷新,感興趣的朋友能夠看個人另外一篇文章 —— 登錄驗證明踐 —— Token & Refresh Token

後記

曾幾什麼時候,被問起項目經驗時,心底裏都有一個疑問:什麼是項目?

我想起了大一時的本身,那時候我以爲項目就是特別高大上的東西,由不少文件組成,彼此之間存在着複雜的關係,必定是不少人一塊兒協做才能搞得出來的。可如今我明白,所謂項目,無非是一個功能集合。分離出的文件最終都會合起來,實現一個完整的功能,而之因此分開,是爲了維護方便。從這點上說,項目不管大小,只要是一個完整的功能,均可以稱做項目,哪怕只有一個簡單的index.html文件。

固然啦,本文針對的主要仍是大型項目中的代碼組織方式,小demo的話怎麼舒服怎麼來。畢竟代碼風格這種東西因人而異,咱們不能說誰的代碼寫的漂亮就必定好。總的來講,良好的代碼風格,有助與排查錯誤,有利於團隊協做,有利於修身養性,有助於...(跑偏了)總之好處不會少。

最後,但願能對你們有所幫助,轉載請附上原文連接

參考

Vuex 官方文檔

相關文章
相關標籤/搜索