Vue的組件溝通方式 在單純的情況之下,兩個上、下層Vue組件若要進行溝通,會使用:
1. props 由上傳下:上層使用 v-bind ,下層用props接到v-bind傳來的屬性資料 由下傳上:下層發出$emit事件,上層則用v-on偵聽該事件
2. eventbus 若專案更大一些,資料不太可能$emit一層一層慢慢傳上去,上層組件用v-on偵聽到事件並拿到資料之後,又再一層層用props慢慢傳到另一個下層組件,這樣很麻煩也很難維護,所以這種結構複雜的跨組件溝通會使用 eventbus 來傳遞資料。
Vuex狀態管理 Vuex 大致上是透過一個 Global state 來儲存整個網站共有的狀態,而其設計概念是由「購買按鈕」組件 commit 一個 Mutation,而這個 Mutation 會改變 State,當 State 被改變時,則會觸發有使用到 State 的「購物車」組件的視覺元件被更新,而這整個觸法的過程是「單向運作」的。
Vuex 跟 eventbus 最主要的差異在於,Vuex 的 Global state 是組件共用的狀態,而非單純儲存在某個組件內部中的狀態。所以本質上,組件與組件之間並沒有溝通,而是透過更新 Global State的狀態,而反應畫面到另外一個組件上。
使用 Vuex 時機
組件之間很上互相溝通:不用Vuex
組件僅需上下傳遞就很夠用:不用Vuex
組件會互相溝通,但情境很固定,次數也很少:Event Bus
組件之間會跨結構傳遞狀態:Vuex
需要全域狀態管理:Vuex
vuex 的 Store 中有四大元素:
State
Mutations
Getters
Actions
解析彼此關係 Actions是指狀態的改變就像是Methods的函式,當State的變數需要做更動時,Actions是無法直接變更的,需經由代理商mutation當作兩者的媒介。
案例:我們可以看到State跟Actions裡的資料完全沒有一點瓜格,都是透過Mutations來連結彼此,逐一解釋三者關聯,
在Mutations裡先命名LOADING為此橋樑的名稱,參數裡載入兩個位置(state預設, actions參數),以此將兩者串連。
actions也要先命名函式名稱updateLoading,並增加兩個參數,前者是函式內使用的變數,第二則成為payload是傳入mutation裡的參數,內容我們先宣告變數名稱要串連mutations的橋樑名稱,以及順帶將payload參數也傳入即可完成
執行流程:html裡寫入vm.$store.dispatch(‘updateLoading’, true),內容主要是呼叫action函式名,並且載入參數true,這時action函式就會進行動作,傳入mutations執行並改變state裡的值,也就是status參數。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 export default new Vuex.Store({ state: { isLoading: false , }, //操作行為 actions:{ updateLoading(context, status) { context.commit('LOADING' , status); }, }, //操作狀態 mutations:{ LOADING(state, status) { //第二個參數是外部傳入 state.isLoading = status; }, } });
主頁面使用dispatch將需求傳至action裡,並帶指定參數
1 vm.$store .dispatch('updateLoading' , true );
如果有要回傳變數,則新增computed裡當作state的接口
1 2 3 4 computed: { cart () { return this.$store .state.cart; },
存放狀態的 State state 是 Vuex.Store 中儲存狀態的物件,裡面就可以存放許多自定義的屬性資料
日後會用到的資料,通常都會事先定義好屬性在 Store 中,即使是空字串、空陣列也沒關係,如果沒有事先定義好,之後就必須要用Vue.set的語法才能在 Store 中新增屬性資料。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const store = new Vuex.Store({ state:{ count:0, userName:"" , list:[] }, mutations:{ addCount(state){ state.count += 1; }, setLoading(state){ //Store 中還沒有 loading 屬性,所以要用 Vue.set 去新增 Vue.set(state,'loading' ,false ); } }, });
變更狀態的 Mutations 要改變 Store 中的屬性,唯一的方法是透過 Mutation,它的實作方法是在 callBack 函式中,把 state 作為參數傳進去,並在其中重新 assign 值給 state 的屬性。而 mutations 中的操作只能是「同步」操作,不能是非同步操作,如果要非同步操作只能在 actions 中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const store = new Vuex.Store({ state:{ count:0, }, mutations:{ addCount(state){ state.count += 1; //不得使用非同步操作,如:fetch // fetch(url).then (()=>{ // state.count +=1; // }); }, }, });
取得狀態的 Getters Getter 其實就是 Store 中的 computed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const store = new Vuex.Store({ state:{ todos:[], }, getters:{ itemsNotDone(state){ return state.todos.filter(item=> !item.done).length; }, //將 getters 拿進 getter 中用 itemsDone(state,getters){ return state.todos.length - getters.itemsNotDone; }, //getter 也可以回傳出一個函式 itemWithID(state){ return ((id)=>{ return state.todos.filter(item=>item.id===id); }) } }, });
在組件中要引用 getters 的做法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 //<script> import {mapStates, mapGetters} from 'vuex' ; export default { computed:{ ...mapStates(['todos' ]), ...mapGetters(['itemNotDone' ,'itemWithID' ]), }, methods:{ test (){ this.itemWithID('123' ); }, }, };
發出指令的 Actions
前面提過,Mutations 裡面的操作只能是同步的,但若要透過「非同步」 的方式改變 State 裡的資料,就必須使用 Actions 來做操作。可是 Action
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 const store = new Vuex.Store({ state:{ todos:[], }, mutations:{ setTodos(state,todos){ state.todos = todos; } }, actions:{ fetchTodos(context,payload){ fetch(url). then (rs => rs.json()). then (todos=>{ context.commit('setTodos' ,todos); }) }, //context 是一個包含 store 中一切的東西, //可是 context 比 store 還多了一些東西, //所以 context 不等同於 store。 //因為我們只需要用到 context 中的 commit //所以這裡經常會使用到重新解構的語法: fetchTodos({commit},payload){ ... .then (todos=>{ commit('setTodos' ,todos); }) ... } } });
在組件中要引用 actions 的做法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 //</script> import { mapActions} from Vuex; export default { mounted (){ this.$store .dispatch('fetchTodos' ,{id:2}); }, //或是將傳入 dispatch 的參數包成一個物件的寫法 mounted (){ this.$store .dispatch({ type :'fetchTodos' ,//type 是固定的 key id:2, }); }, methods:{ ...mapActions(['fetchTodos' ]) } };
mapAction語法 注意:如果是要帶參數就不可以使用此語法
使用mapAction語法,需在script下新增
1 import { mapGetters, mapActions } from 'vuex' ;
並在method裡的呼叫語法裡刪除既有的dispatch語法直接改成mapAction支援的格式 …mapActions([‘getProducts’]),