一、問題html
能夠想象到在簡單的 父子
,子父
組件之間的通訊是很輕鬆的,經過 props
和 events
便可實現;可是每每咱們的應用可能不僅有這麼簡單的層級關係,在多層跨級組件若是經過 props
去傳遞,那意味着一層一層的往子組件傳遞,最終你可能不知道當前組件的數據最終來自哪一個父組件(固然你能夠逆着方向一層一層往上找),經過 events
事件機制顯然也存在着相似的問題。若是你以爲這樣也能夠接受,你可能不須要 Vuex;但若是你在想有沒有什麼好的模式優雅的去解決,你能夠繼續閱讀下面的部分。vue
二、vuexjava
vue提供了另一個相似 Redux 的解決方案 Vuex,一個集中式狀態管理的庫;也就是說,你可能不須要 Vuex,它只是對你應用狀態進行管理的一個庫。vuex
vuex關鍵詞:緩存
集中式狀態管理模式(注意是強調管理應用的全部組件的狀態)異步
可預測(前提是以相應的規則做爲保證)ide
三、集中式狀態管理模式函數
在說集中式管理模式以前,咱們能夠先來想一想常見的處理方式是怎樣的,即每一個組件維護自身的數據和狀態,自給自足,分而治之;其思路大體以下:工具
定義組件自身的初始數據
在組件內獲取異步數據
根據數據渲染更新視圖
但事情並不是那麼完美,因爲這種方式封裝的組件的內部實現聚合了異步請求的數據和自身的狀態,真正組裝複用起來是存在必定問題的。好比:
在同一可視區域的冗餘請求數
不一樣層級組件的數據共享問題
集中式狀態管理模式則以一個全局單例模式管理應用的狀態,相似於全局對象,但不徹底同樣。
Vuex 的狀態管理存儲是響應式的:就是當你的組件使用到了 Vuex 的某個狀態,一旦它發生改變了,全部關聯的組件都會自動更新相對應的數據。
不能直接修改 Vuex 的狀態:修改 Vuex 的狀態惟一途徑是提交(commit) mutations 來實現修改。
Vue Components:Vue組件。HTML頁面上,負責接收用戶操做等交互行爲,執行dispatch方法觸發對應action進行迴應。
dispatch:操做行爲觸發方法,是惟一能執行action的方法。
actions:操做行爲處理模塊。負責處理Vue Components接收到的全部交互行爲。包含同步/異步操做,支持多個同名方法,按照註冊的順序依次觸發。向後臺API請求的操做就在這個模塊中進行,包括觸發其餘action以及提交mutation的操做。該模塊提供了Promise的封裝,以支持action的鏈式觸發。
commit:狀態改變提交操做方法。對mutation進行提交,是惟一能執行mutation的方法。
mutations:狀態改變操做方法。是Vuex修改state的惟一推薦方法,其餘修改方式在嚴格模式下將會報錯。該方法只能進行同步操做,且方法名只能全局惟一。操做之中會有一些hook暴露出來,以進行state的監控等。
state:頁面狀態管理容器對象。集中存儲Vue components中data對象的零散數據,全局惟一,以進行統一的狀態管理。頁面顯示所需的數據從該對象中進行讀取,利用Vue的細粒度數據響應機制來進行高效的狀態更新。
getters:state對象讀取方法。圖中沒有單獨列出該模塊,應該被包含在了render中,Vue Components經過該方法讀取全局state對象。
流程:Vue組件接收交互行爲,調用dispatch方法觸發action相關處理,若頁面狀態須要改變,則調用commit方法提交mutation修改state,經過getters獲取到state新值,從新渲染Vue Components,界面隨之更新
集中式存儲管理應用的全部狀態,按照字面意思是將全部的狀態都集中式管理,也就是存到 Vuex 的全局單一 store 中,顯然咱們是不能這樣去理解的,應該視應用場景而定的,大體也能夠分爲如下幾種:
對於用戶輸入的狀態,好比控制模態框的顯示隱藏,咱們通常在組件內處理消化;對於須要須要跨組件通訊的,則能夠存儲在全局的 store 中,咱們能夠將這一類狀態稱之爲本地狀態(local state)。
對於服務端傳過來的數據狀態,按照大多數的實踐是存儲在全局的 store 中,這樣能夠在任意的組件中均可以使用;固然,也能夠只將多組件的共享的數據存儲在全局的 store 中,單個組件須要的數據內部處理消化,組件銷燬時對應的數據狀態也會銷燬。
說明:
(1)能夠直接操做state
<a href="javascript:;" @click="$store.state.show = true">點擊</a>
在嚴格模式下,不管什麼時候發生了狀態變動且不是由 mutation 函數引發的,將會拋出錯誤。
(2)若是在 mutations 裏執行異步操做會發生什麼事情 , 實際上並不會發生什麼奇怪的事情 , 只是官方推薦 , 不要在 mutationss 裏執行異步操做而已。
(3)多個 state 的操做 , 使用 mutations 會來觸發會比較好維護 , 那麼須要執行多個 mutations 就須要用 action 了
actions: { switch_dialog (context) { // 這裏的context和咱們使用的$store擁有相同的對象和方法
context.commit('switch_dialog') // 你還能夠在這裏觸發其餘的mutations方法
},
(4)不少時候 , $store.state.dialog.show 、$store.dispatch('switch_dialog') 這種寫法又長又臭 , 很不方便 , 咱們沒使用 vuex 的時候 , 獲取一個狀態只須要 this.show , 執行一個方法只須要 this.switch_dialog 就好了 , 使用 vuex 使寫法變複雜了 ?
<template>
<el-dialog :visible.sync="show"></el-dialog>
</template>
<script> import {mapState} from 'vuex' export default { computed:{ //這裏的三點叫作 : 擴展運算符
...mapState({ show: state => state.dialog.show }), } } </script>
mapState
函數返回的是一個對象。
(5)、getter:返回值會根據它的依賴被緩存起來,且只有當它的依賴值發生了改變纔會被從新計算。
(6)、mapGetters
輔助函數僅僅是將 store 中的 getter 映射到局部計算屬性:
import { mapGetters } from 'vuex' export default { // ...
computed: { // 使用對象展開運算符將 getter 混入 computed 對象中
...mapGetters([ 'doneTodosCount', 'anotherGetter', // ...
]) } }
(7)、使用常量替代 Mutation 事件類型
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import Vuex from 'vuex' import { SOME_MUTATION } from './mutation-types' const store = new Vuex.Store({ state: { ... }, mutations: { // 咱們可使用 ES2015 風格的計算屬性命名功能來使用一個常量做爲函數名
[SOME_MUTATION] (state) { // mutate state
} } })
(8)mapMutations
你能夠在組件中使用 this.$store.commit('xxx')
提交 mutation,或者使用 mapMutations
輔助函數將組件中的 methods 映射爲 store.commit
調用(須要在根節點注入 store
)。
import { mapMutations } from 'vuex' export default { // ...
methods: { ...mapMutations([ 'increment', // 將 `this.increment()` 映射爲 `this.$store.commit('increment')`
// `mapMutations` 也支持載荷:
'incrementBy' // 將 `this.incrementBy(amount)` 映射爲 `this.$store.commit('incrementBy', amount)`
]), ...mapMutations({ add: 'increment' // 將 `this.add()` 映射爲 `this.$store.commit('increment')`
}) } }
(9)Action
Action 函數接受一個與 store 實例具備相同方法和屬性的 context 對象,所以你能夠調用 context.commit
提交一個 mutation,或者經過 context.state
和 context.getters
來獲取 state 和 getters。當咱們在以後介紹到 Modules 時,你就知道 context 對象爲何不是 store 實例自己了。
寫法一:
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } }, actions: { increment (context) { context.commit('increment') } } })
參數解構寫法:
actions: { increment ({ commit }) { commit('increment') } }
(10)mapActions
你在組件中使用 this.$store.dispatch('xxx')
分發 action,或者使用 mapActions
輔助函數將組件的 methods 映射爲 store.dispatch
調用(須要先在根節點注入 store
):
import { mapActions } from 'vuex' export default { // ...
methods: { ...mapActions([ 'increment', // 將 `this.increment()` 映射爲 `this.$store.dispatch('increment')`
// `mapActions` 也支持載荷:
'incrementBy' // 將 `this.incrementBy(amount)` 映射爲 `this.$store.dispatch('incrementBy', amount)`
]), ...mapActions({ add: 'increment' // 將 `this.add()` 映射爲 `this.$store.dispatch('increment')`
}) } }
(11)module
const moduleA = { state: { ... }, mutations: { ... }, actions: { ... }, getters: { ... } } const moduleB = { state: { ... }, mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } }) store.state.a // -> moduleA 的狀態
store.state.b // -> moduleB 的狀態
# 模塊的局部狀態
對於模塊內部的 mutation 和 getter,接收的第一個參數是模塊的局部狀態對象。
const moduleA = { state: { count: 0 }, mutations: { increment (state) { // 這裏的 `state` 對象是模塊的局部狀態
state.count++ } }, getters: { doubleCount (state) { return state.count * 2 } } }
一樣,對於模塊內部的 action,局部狀態經過 context.state
暴露出來,根節點狀態則爲 context.rootState
:
const moduleA = { // ...
actions: { incrementIfOddOnRootSum ({ state, commit, rootState }) { if ((state.count + rootState.count) % 2 === 1) { commit('increment') } } } }
對於模塊內部的 getter,根節點狀態會做爲第三個參數暴露出來:
const moduleA = { // ...
getters: { sumWithRootCount (state, getters, rootState) { return state.count + rootState.count } } }
2019.3.22更新:
使用action 來分發 (dispatch) 事件通知 store 去改變,這樣約定的好處是,咱們可以記錄全部 store 中發生的 state 改變,同時實現能作到記錄變動 (mutation)、保存狀態快照、歷史回滾/時光旅行的先進的調試工具。