vuex的應用場景


vuex 作為 vue 生態中用於狀態管理的一種模式,已被廣泛應用於 vue 單頁應用開發中。下面談談自己對 vuex 的一些個人見解以及在實際項目中的應用場景。


vuex 的幾個核心概念Store:Vuex 使用一個 Store 對象管理應用的狀態,一個 Store 包括 State, Getter, Mutation, Action 四個屬性。
State:State 意為“狀態”,是 vuex 狀態管理的數據源,我個人覺得相當於數據庫。
Getter:Getter 的作用與 filters 有一些相似,可以將 State 進行過濾后輸出。(參考:https://www.jb51.net/article/159727.htm)

1 應用場景

假設我們在 Vuex 中定義了一個數組

1
2
3
4
5
6
const store = new Vuex.Store({
   state: {
     list:[1,3,5,7,9,20,30]
   }
  ...
})

業務場景希望過濾出大於 5 的數。馬上想到的方法可能的是:在組件的計算屬性中進行過濾:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
   <div>
     {{list}}
   </div>
</template>
<script>
   export default {
     name: "index.vue" ,
     computed: {
       list() {
         return this .$store.state.list.filter(item => item > 5);
       }
     }
  }
</script>

效果:

功能雖然實現了,但如果其它組件也需要過濾后的數據,那么就得把 index.vue 中的計算過濾代碼復制出來。如果過濾規則發生變化,還得一一修改這些組件中的計算屬性,很難維護。這種場景下,我們就可以使用 getters 屬性啦O(∩_∩)O~

2 基礎用法

main.js:

1
2
3
4
5
6
7
8
9
10
const store = new Vuex.Store({
   state: {
     list: [1, 3, 5, 7, 9, 20, 30]
   },
   getters: {
     filteredList: state => {
       return state.list.filter(item => item > 5)
     }
   }
})

index.vue:

1
2
3
4
5
6
7
8
9
10
<script>
   export default {
     name: "index.vue" ,
     computed: {
       list() {
         return this .$store.getters.filteredList;
       }
     }
   }
</script>

效果達到了,而且只需要在一處維護過濾規則即可。

3 內部依賴

getter 可以依賴其它已經定義好的 getter。比如我們需要統計過濾后的列表數量,就可以依賴之前定義好的過濾函數。

main.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
const store = new Vuex.Store({
   state: {
     list: [1, 3, 5, 7, 9, 20, 30]
   },
   getters: {
     filteredList: state => {
       return state.list.filter(item => item > 5)
     },
     listCount: (state, getters) => {
       return getters.filteredList.length;
     }
   }
})

index.vue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
 
   <div>
     過濾后的列表:{{list}}
     <br>
     列表長度:{{listCount}}
   </div>
</template>
 
<script>
   export default {
     name: "index.vue" ,
     computed: {
       list() {
         return this .$store.getters.filteredList;
       },
       listCount() {
         return this .$store.getters.listCount;
       }
     }
   }
</script>

效果:


Mutation:Mutaion 是 vuex 中改變 State 的唯一途徑(嚴格模式下),並且只能是同步操作。

Action:一些對 State 的異步操作和業務邏輯操作可以放在 Action 中,並通過在 Action 提交 Mutaion 變更狀態。見下面用戶登錄和用戶信息demo

Module:當 Store 對象過於龐大時,可根據具體的業務需求分為多個 Module。

關於用戶登錄和用戶信息模塊的module

  1 import storage from 'store'
  2 import { login, getInfo, logout } from '@/api/login'
  3 import { ACCESS_TOKEN } from '@/store/mutation-types'
  4 import { welcome } from '@/utils/util'
  5 import Vue from 'vue'
  6 
  7 const user = {
  8   state: {
  9     token: '',
 10     name: '',
 11     welcome: '',
 12     avatar: '',
 13     roles: [],
 14     info: {}
 15   },
 16 
 17   mutations: {
 18     SET_TOKEN: (state, token) => {
 19       state.token = token
 20     },
 21     SET_NAME: (state, { name, welcome }) => {
 22       state.name = name
 23       state.welcome = welcome
 24     },
 25     SET_AVATAR: (state, avatar) => {
 26       state.avatar = avatar
 27     },
 28     SET_ROLES: (state, roles) => {
 29       state.roles = roles
 30     },
 31     SET_INFO: (state, info) => {
 32       state.info = info
 33     }
 34   },
 35 
 36   actions: {
 37     // 登錄
 38     Login({ commit }, userInfo) {
 39       return new Promise((resolve, reject) => {
 40         login(userInfo).then(response => {
 41           const result = response.result
 42           storage.set(ACCESS_TOKEN, result.token, 7 * 24 * 60 * 60 * 1000)
 43           commit('SET_TOKEN', result.token)
 44           resolve()
 45         }).catch(error => {
 46           reject(error)
 47         })
 48       })
 49     },
 50     
 51     // 獲取用戶信息
 52     GetInfo({ commit }) {
 53       return new Promise((resolve, reject) => {
 54         getInfo().then(response => {
 55           const result = response.result
 56 
 57           if (result.role && result.role.permissions.length > 0) {
 58             const role = result.role
 59             role.permissions = result.role.permissions
 60             role.permissions.map(per => {
 61               if (per.actionEntitySet != null && per.actionEntitySet.length > 0) {
 62                 const action = per.actionEntitySet.map(action => { return action.action })
 63                 per.actionList = action
 64               }
 65             })
 66             role.permissionList = role.permissions.map(permission => { return permission.permissionId })
 67             commit('SET_ROLES', result.role)
 68             commit('SET_INFO', result)
 69           } else {
 70             reject(new Error('getInfo: roles must be a non-null array !'))
 71           }
 72 
 73           commit('SET_NAME', { name: result.name, welcome: welcome() })
 74           commit('SET_AVATAR', result.avatar)
 75 
 76           resolve(response)
 77         }).catch(error => {
 78           reject(error)
 79         })
 80       })
 81     },
 82 
 83     // 登出
 84     Logout({ commit, state }) {
 85       return new Promise((resolve) => {
 86         logout(state.token).then(() => {
 87           resolve()
 88         }).catch(() => {
 89           resolve()
 90         }).finally(() => {
 91           commit('SET_TOKEN', '')
 92           commit('SET_ROLES', [])
 93           storage.remove(ACCESS_TOKEN)
 94         })
 95       })
 96     }
 97 
 98   }
 99 }
100 
101 export default user

每個module下state里的數據,綁定到根store上

const getters = {
  isMobile: state => state.app.isMobile,
  lang: state => state.app.lang,
  theme: state => state.app.theme,
  qualityCheckInfo: state => state.app.qualitycCheckBaseInfo,
  color: state => state.app.color,
  token: state => state.user.token,
  avatar: state => state.user.avatar,
  nickname: state => state.user.name,
  welcome: state => state.user.welcome,
  roles: state => state.user.roles,
  userInfo: state => state.user.info,
  addRouters: state => state.permission.addRouters,
  multiTab: state => state.app.multiTab
}

export default getters

導出store的index.js文件

 1 import Vue from 'vue'
 2 import Vuex from 'vuex'
 3 
 4 import app from './modules/app'
 5 import user from './modules/user'
 6 
 7 // default router permission control
 8 import permission from './modules/permission'
 9 
10 // dynamic router permission control (Experimental)
11 // import permission from './modules/async-router'
12 import getters from './getters'
13 
14 Vue.use(Vuex)
15 
16 export default new Vuex.Store({
17   modules: {
18     app,
19     user,
20     permission
21   },
22   state: {
23 
24   },
25   mutations: {
26 
27   },
28   actions: {
29 
30   },
31   getters
32 })

 


上圖為官網中 vuex 各個要素的關系圖,總的來說,我們可以在組件中觸發 Action,Action 則會提交 Mutation,Mutaion 會對 State 進行修改,組件再根據 State 、Getter 渲染頁面。

什么樣的應用場景下需要 vuex ?如果不打算開發大型單頁應用,使用 Vuex 可能是繁瑣冗余的。確實是如此——如果你的應用夠簡單,那最好不要使用 Vuex。一個簡單的 global event bus 就足夠所需了。但是,如果需要構建是一個中大型單頁應用,很可能會考慮如何更好地在組件外部管理狀態,Vuex 將會成為自然而然的選擇。

vuex 一般用於中大型 web 單頁應用中對應用的狀態進行管理,對於一些組件間關系較為簡單的小型應用,使用 vuex 的必要性不是很大,因為完全可以用組件 prop 屬性或者事件來完成父子組件之間的通信,vuex 更多地用於解決跨組件通信以及作為數據中心集中式存儲數據。

使用 vuex 解決跨組件通信問題
跨組件通信一般指非父子組件間的通信,父子組件的通信一般可以通過以下方式:
1、通過 prop 屬性實現父組件向子組件傳遞數據
2、通過在子組件中觸發事件實現向父組件傳遞數據
非父子組件之間的通信一般通過一個空的 Vue 實例作為 中轉站,也可以稱之為 事件中心、event bus。

// 創建事件中心實例
let bus = new Vue()

// 在組件 A 中觸發事件
bus.$emit('test', 1)

// 在組件 B 中接受事件
bus.$on('test', (id) => {
// ...
})

采用 event bus 的方式適合簡單的跨組件事件觸發,對於多層級組件嵌套等較為復雜的場景,使用 vuex 能更好地應對。vuex 是通過將 state 作為數據中心、各個組件共享 state 實現跨組件通信的,此時的數據完全獨立於組件,因此將組件間共享的數據置於 State 中能有效解決多層級組件嵌套的跨組件通信問題。

vuex 作為數據存儲中心
vuex 的 State 在單頁應用的開發中本身具有一個“數據庫”的作用,可以將組件中用到的數據存儲在 State 中,並在 Action 中封裝數據讀寫的邏輯。這時候存在一個問題,一般什么樣的數據會放在 State 中呢? 目前主要有兩種數據會使用 vuex 進行管理:
1、組件之間全局共享的數據
2、通過后端異步請求的數據

本人所在團隊在實際的項目開發過程中更多的是應用第二種,即把通過后端異步請求的數據都納入 vuex 狀態管理,在 Action 中封裝數據的增刪改查等邏輯,這樣可以一定程度上對前端的邏輯代碼進行分層,使組件中的代碼更多地關注頁面交互與數據渲染等視圖層的邏輯,而異步請求與狀態數據的持久化等則交由 vuex 管理。

總結:vuex 具體應用在哪取決於項目的規模以及具體的業務場景,可能是為了解決多層嵌套組件之間的通信問題,或是為了更好地管理應用中錯綜復雜的狀態關系,而不能為了用 vuex 而在項目中使用 vuex。

參考: https://zhuanlan.zhihu.com/p/41237859

見下面用戶登錄和用戶信息demo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM