還是像以前一樣用一個簡單的案例來解釋vuex
首先,新建一個模板demo
1 vue init webpack-simple demo
我們需要兩個組件:一個輸入組件和一個顯示組件
在src文件夾中新建一個components文件夾,添加一個showInfo.vue
1 <template> 2 <h1>{{ msg }}</h1> 3 </template> 4 5 <script> 6 export default { 7 props: ['msg'] 8 } 9 </script>
然后,再添加一個changeInfo.vue
1 <template> 2 <input type="text" :value="msg"> 3 </template> 4 5 <script> 6 export default { 7 props: ['msg'] 8 } 9 </script>
修改App.vue:
1 <template> 2 <div> 3 <show-info :msg="msg"></show-info> 4 <change-info :msg="msg"></change-info> 5 </div> 6 </template> 7 8 <script> 9 import showInfo from './components/showInfo' 10 import changeInfo from './components/changeInfo' 11 12 export default { 13 components: { 14 showInfo, 15 changeInfo 16 }, 17 data() { 18 return { 19 msg: 'Hello Vue!' 20 } 21 } 22 } 23 </script>
運行起來或許是這樣的:
缺點是信息傳遞仍然是使用綁定:msg="msg",而且由於vue組件之間傳輸信息的限制,現在修改輸入組件的文本並不會改變顯示組件的值。
現在來使用vuex來傳遞變量信息,首先安裝vuex:
1 npm install vuex --save
然后在src文件夾下新建vuex文件夾,並添加store.js
1 import Vue from 'vue' 2 import Vuex from 'vuex' 3 Vue.use(Vuex) 4 5 // 添加兩個常量state和mutations 6 const state = { 7 msg: 'Hello Vue!' 8 } 9 10 const mutations = { 11 changeMessage(state, msg) { 12 state.msg = msg 13 } 14 } 15 16 // 初始化已經創建的常量 17 export default new Vuex.Store({ 18 state: state, 19 mutations: mutations 20 })
注意,在ES6中{state: state, mutations: mutations}可以簡寫為{state, mutations}
然后修改App.vue:
然后,在src/components/showInfo.vue中需要使用計算屬性來代替props:
1 <template> 2 <h1>{{ msg }}</h1> 3 </template> 4 5 <script> 6 export default { 7 computed: { 8 msg() { 9 return this.$store.state.msg 10 } 11 } 12 } 13 </script>
在changeInfo.vue中需要額外添加changeMsg方法:
1 <template> 2 <input type="text" :value="msg" @keyup="changeMsg"> 3 </template> 4 5 <script> 6 export default { 7 computed: { 8 msg() { 9 return this.$store.state.msg 10 } 11 }, 12 methods: { 13 changeMsg(e) { 14 this.$store.commit('changeMessage', e.target.value) 15 } 16 } 17 } 18 </script>
來看一下運行效果:
然而,現在的代碼是非常不好維護的,想象一下下面兩個場景:
1) 在一個大規模的應用中如果不同的組件都使用了$this.store.state.somevalue,一旦我們修改了somevalue的名字,就需要修改所有用到它的組件。
2) 如果有一個需要計算的值,例如計數器counter,如果其他的組件需要對它執行+1操作的話就得在組件中添加一個單獨的方法,導致代碼冗余。
首先,為了解決第一個問題,我們需要在vuex中引入一個getters.js文件:
1 export default { 2 getMessage(state) { 3 return state.msg 4 } 5 }
然后在store.js中引入getters.js
然后在兩個組件中修改引用store.state.msg方法
1 computed: { 2 msg() { 3 return this.$store.getters.getMessage 4 } 5 }
遺憾的是this.$store.getters這個符號仍然很長,我們一般都希望使用短一點的名字來表示變量,好消息是vue提供了mapGetters來幫助我們映射變量名,僅僅只需要引入並修改計算屬性:
1 <!-- showInfo.vue --> 2 <template> 3 <h1>{{ getMessage }}</h1> 4 </template> 5 6 <script> 7 import { mapGetters } from 'vuex' 8 export default { 9 computed: mapGetters(['getMessage']) 10 } 11 </script>
不過一個更好的方法是使用對象來代替mapGetters中的數組:
1 <!-- showInfo.vue --> 2 <template> 3 <h1>{{ msg }}</h1> 4 </template> 5 6 <script> 7 import { mapGetters } from 'vuex' 8 export default { 9 computed: mapGetters({ 10 msg: 'getMessage' 11 }) 12 } 13 </script>
現在,讓我們來體驗一下這種修改的好處吧。如果我們需要將輸入的所有字母轉化為大寫,只需要做一處修改src/vuex/getters.js:
1 export default { 2 getMessage(state) { 3 return (state.msg).toUpperCase() 4 } 5 }
這就像魔法一樣神奇!接着來解決第二個問題。
首先在store.js中添加counter屬性和相應的方法:
因此,在getters.js文件中:
1 export default { 2 getMessage(state) { 3 return (state.msg).toUpperCase() 4 }, 5 getCounter(state) { 6 return (state.counter) 7 } 8 }
同時,在顯示組件showInfo.vue上顯示counter:
1 <template> 2 <div> 3 <h1>{{ msg }}</h1> 4 <div>The message was changed {{ counter }} times</div> 5 </div> 6 </template> 7 8 <script> 9 import { mapGetters } from 'vuex' 10 export default { 11 computed: mapGetters({ 12 msg: 'getMessage', 13 counter: 'getCounter' 14 }) 15 } 16 </script>
然后,在src/vuex中添加actions.js來管理函數調用的動作:
1 export default { 2 CHANGE_MSG({ commit }, msg) { 3 commit('changeMessage', msg) 4 }, 5 ADD_COUNTER({ commit }) { 6 commit('addCounter') 7 } 8 }
然后像引用getters.js一樣在store.js中引用actions.js(同樣也用mapActions映射函數名),最后在changeInfo.vue組件中引用這兩個action:
1 <!-- changeInfo.vue --> 2 <template> 3 <input type="text" :value="msg" 4 @keyup="chMsg($event.target.value)" 5 @keyup.enter="addCnt"> 6 </template> 7 8 <script> 9 import { mapGetters } from 'vuex' 10 import { mapActions } from 'vuex' 11 export default { 12 computed: mapGetters({ 13 msg: 'getMessage' 14 }), 15 methods: mapActions({ 16 chMsg: 'CHANGE_MSG', 17 addCnt: 'ADD_COUNTER' 18 }) 19 } 20 </script>