我們的項目非常簡單,當點擊+1按鈕的時候,count 加1,點擊-1按鈕的時候,count 減1.
1, mutation
The only way to actually change state in a Vuex store is by committing a mutation, 在vue 中,只有mutation 才能改變state. mutation 類似事件,每一個mutation都有一個類型和一個處理函數,因為只有mutation 才能改變state, 所以處理函數自動會獲得一個默認參數 state. 所謂的類型其實就是名字,action去comit 一個mutation, 它要指定去commit哪個mutation, 所以mutation至少需要一個名字,commit mutation 之后, 要做什么事情,那就需要給它指定一個處理函數, 類型(名字) + 處理函數就構成了mutation. 現在store.js添加mutation.
const store = new Vuex.Store({ state: { count:0 }, mutations: { // 加1 increment(state) { state.count++; }, // 減1 decrement(state) { state.count-- } } })
Vue 建議我們mutation 類型用大寫常量表示,修改一下,把mutation 類型改為大寫
mutations: { // 加1 INCREMENT(state) { state.count++; }, // 減1 DECREMENT(state) { state.count-- } }
2, action
action去commit mutations, 所以還要定義action. store.js 里面添加actions.
const store = new Vuex.Store({ state: { count:0 }, mutations: { // 加1 INCREMENT(state) { state.count++; }, // 減1 DECREMENT(state) { state.count-- } }, actions: { increment(context) { context.commit("INCREMENT"); }, decrement(context) { context.commit("DECREMENT"); } } })
action 和mutions 的定義方法是類似的,我們要dispatch 一個action, 所以actions 肯定有一個名字,dispatch action 之后它要做事情,就是commit mutation, 所以還要給它指定一個函數。因為要commit mutation ,所以 函數也會自動獲得一個默認參數context, 它是一個store 實例,通過它可以獲取到store 實例的屬性和方法,如 context.state 就會獲取到 state 屬性, context.commit 就會執行commit命令。
其實actions 還可以簡寫一下, 因為函數的參數是一個對象,函數中用的是對象中一個方法,我們可以通過對象的解構賦值直接獲取到該方法。修改一下 actions
actions: { increment({commit}){ commit("INCREMENT") }, decrement({commit}){ commit("DECREMENT") } }
3, dispatch action
現在就剩下dispatch action 了。什么時候dispatch action 呢? 只有當我們點擊按鈕的時候. 給按鈕添加click 事件,在click 事件處理函數的中dispatch action.
打開increment.vue 組件,給兩個按鈕添加click 事件。
<template> <div> <button @click="increment">+1</button> <button @click="decrement">-1</button> </div> </template> <script> export default { methods: { increment(){ this.$store.dispatch("increment"); }, decrement() { this.$store.dispatch("decrement") } } } </script>
其實像上面dispatch action 比較麻煩,如果有10 個按鈕,我們要寫10 個函數,且存在大量的重復,並且我們的事件處理函數名字和action的名字是一樣的,這時vue 提供了mapAction 函數,它和mapState 是一樣的,把我們的 action 直接映射到store 里面的action中。
像這種組件中的事件處理函數名字和action的名字是相同的,直接把 事件處理函數名字放到一個數組中。組件中的methods 修改如下:
<script> import {mapActions} from "vuex"; export default { methods: { ...mapActions(["increment", "decrement"]) } } </script>
如果事件處理函數名字和action的名字不同,給mapActions 提供一個對象,對象的屬性是事件處理函數名字, 屬性值是 對應的dispatch 的action 的名字。
我們把 +1 按鈕的事件處理函數變改為 add,代碼如下:
<template> <div> <button @click="add">+1</button> <!-- 事件處理函數變為add --> <button @click="decrement">-1</button> </div> </template> <script> import {mapActions} from "vuex"; export default { methods: { ...mapActions(["decrement"]),
// mapActions 對應做出改變 ...mapActions({ add: "increment" }) } }
這時候我們單擊按鈕,就可以看到count 發生變化。
通過vuex 傳遞參數
很多時候,組件和組件之間還要傳遞參數,這些都要經過vuex。 在increment 組件內部增加一個輸入框和一個按鈕,點擊按鈕的時候,count 增加輸入框內的值。
<template> <div> <div> <button @click="increment">+1</button> <button @click="decrement">-1</button> </div>
// 增加內容 <div> <input type="text" v-model="incrementValue"> <button @click="incrementWithValue">increment</button> </div> </div> </template>
在組件內部dispatch action 的時候,它可以自定義參數,只要參數在它dispatch 的action名稱 后面,依次列出來就可以了。 在這里,我們點擊按鈕的時候,觸發一個incrementWithValue action, 並且帶一個參數,就可以這樣寫 this.$store.dispatch(“incrementWithValue”, value), 整個increment.vue 組件如下:
<template> <div> <div> <button @click="increment">+1</button> <button @click="decrement">-1</button> </div> <div> <input type="text" v-model="incrementValue"> <button @click="incrementWithValue">increment</button> </div> </div> </template> <script> import {mapActions} from "vuex"; export default { data() { return { incrementValue: 0 } }, methods: { ...mapActions(["increment","decrement"]), incrementWithValue() { this.$store.dispatch("incrementWithValue", this.incrementValue) } } } </script>
同樣,actions 和mutations 中的處理函數也是一樣,它除了可以得到默認參數外,還可以接受自定義的參數,我們自定義的參數,依次在默認參數后面列出來就可以了。 在store.js中分加增加incrementWithValue action和 INCREMENT_WITH_VALUE mutation
const store = new Vuex.Store({ state: { count:0 }, mutations: { // 加1 INCREMENT(state) { state.count++; }, // 減1 DECREMENT(state) { state.count-- }, INCREMENT_WITH_VALUE(state, value){ state.count +=value; } }, actions: { increment({commit}){ commit("INCREMENT") }, decrement({commit}){ commit("DECREMENT") }, incrementWithValue({commit}, value){ commit("INCREMENT_WITH_VALUE", parseInt(value)) } } })
錯誤處理
當我們給vuex 傳參的時候,我們要檢測參數的正確性,如果有錯誤需要進行處理
action 中如果是同步操作,就用try..catch 語句,組件中使用try…catch, 捕獲action中拋出的錯誤。Increment.vue 組件中,incrementWithValue() 方法中修改如下:
methods: { ...mapActions(["increment","decrement"]), incrementWithValue() { try { this.$store.dispatch("incrementWithValue", this.incrementValue) }catch(error) { alert(error) } } }
同時store.js 中的action 也進行如下修改:
incrementWithValue({commit}, value){ let intValue = parseInt(value); if(isNaN(intValue)){ throw "Not an Interger" }else { commit("INCREMENT_WITH_VALUE", intValue) } }
如果action 中進行的是異步操作,那就需要在回調函數中進行錯誤處理。
incrementWithValue({commit}, value){ let intValue = parseInt(value) setTimeout(function() { if(isNaN(intValue)) { alert("Not an Interger") }else { commit("INCREMENT_WITH_VALUE", intValue) } }, 2000) }
異步操作給出用戶提示信息
首先,在我們的increment.vue中添加一個提示信息,簡單給一個div 進行提示。用戶提示信息的顯示和隱藏,又涉及到一個狀態,我們設為waiting, 需要在state 中進行添加。默認為false, 同時我們組件需要從state 中獲取到初始狀態。increment.vue 組件修改如下:
<template> <div> <div> <button @click="increment">+1</button> <button @click="decrement">-1</button> </div> <div> <input type="text" v-model="incrementValue"> <button @click="incrementWithValue">increment</button> </div> <!-- 展示信息 --> <div v-if ="show"> waiting </div> </div> </template> <script> import {mapActions} from "vuex"; export default { data() { return { incrementValue: 0 } },
// computed 從state 中獲取初始狀態 computed: { show: function() { return this.$store.state.waiting; } }, methods: { ...mapActions(["increment","decrement"]), incrementWithValue() { this.$store.dispatch("incrementWithValue", this.incrementValue) } } } </script>
mutation 去操作狀態,所以增加兩個muatation, 用於顯示和隱藏waiting. action 中去觸發這兩個mutation. 整個state 如下:
const store = new Vuex.Store({ state: { count:0, // 新增waiting 狀態 waiting: false }, mutations: { // 加1 INCREMENT(state) { state.count++; }, // 減1 DECREMENT(state) { state.count-- }, INCREMENT_WITH_VALUE(state, value){ state.count +=value; }, // 顯示和隱藏waiting SHOW_WAITING_MESSAGE(state){ state.waiting = true; }, HIDE_WAITING_MESSAGE(state){ state.waiting = false; } }, actions: { increment({commit}){ commit("INCREMENT") }, decrement({commit}){ commit("DECREMENT") }, incrementWithValue({commit}, value){ commit("SHOW_WAITING_MESSAGE"); let intValue = parseInt(value) setTimeout(function() { if(isNaN(intValue)) { alert("Not an Interger") }else { commit("HIDE_WAITING_MESSAGE"); commit("INCREMENT_WITH_VALUE", intValue) } }, 2000) } } })
注意:
mutation是同步的,只要comit muation, 它就會立即改變state , 這有利於我們追蹤 狀態的改變。如果 mution 之后,五分鍾才改變state, 那就真不知道state 到底是哪個state了。
action 是異步的,還有的錯誤處理也都在這里操作。