iT邦幫忙

2021 iThome 鐵人賽

DAY 24
0
Modern Web

我的Vue學習筆記系列 第 24

Day24-Vuex核心概念與結構(part2)

接續前面的內容,還有幾個東西沒有研究到...

3. mutations

前面是學到的向倉庫取資料的辦法,再來就是要學怎麼寫資料進去倉庫。唯一方法是提交(commit)某個mutations屬性,在裡面定義一個function和callback,其中有兩個參數,分別為state與payload(傳遞進來的值)。

state: {
    product: 't-shirt',
    price: 500,
    quantity: 100
  },
mutations: {
  setQuantity(state, payload) {
    state.quantity = payload
  }
},

若今天要更新state.quantity時,可以在元件新增且綁定到按鈕點擊事件上。點擊按鈕,元件會提交(commit) setQuantity給mutations,再把this.qty傳出去。

<template>
  <input type="text" v-model.number="qty" />
	 <button @click="unpdateQty">Click</button>
</template>

<script>
export default {
  data() {
    return {
      qty: 0,
    };
  },
  methods: {
    updateQty() {
      this.$store.commit("setQuantity", this.qty);
    },
  },
};
</script>

另外,想要傳物件的話,可以這樣寫

this.$store.commit({
	type: 'setQuantity',
	qty: this.qty
})

對應的mutations也要改

mutations: {
  setQuantity(state, payload) {
    state.quantity = payload.qty
  }
},

mapMutations

和前兩個一樣。透過mapMutations將setQuantity引入元件,mapMutations回傳一個對應的setQuantity方法,並自動帶入payload功能。

<template>
  <input type="text" v-model.number="qty" />
	//此處有多帶參數
  <button @click="setQuantity(qty)">Click</button>
</template>

<script>
import { mapMutations } from "vuex";

export default {
  data() {
    return {
      qty: 0,
    };
  },
  methods: {
    ...mapMutations(["setQuantity"]),
  },
};
</script>

注意!! mapMutations中所有操作必須為同步,故無法在裡面使用async/await或promise等非同步操作。

4. actions

作用類似mutations,但不能直接操控state的資料,也因此在這裡可以執行非同步的任務,再把結果傳回mutations去改資料。

接續範例,state.product變成空物件,在actions加上fetchProductInf函式

state: {
    product: {}
  },
actions: {
  fetchProductInfo(context,payload){
		//假設API回傳{"id":"123", "name": "t-shirt", "price": 500, "quantity": 100}
    fetch('...')
			.then(res=>res.json)
			.then(data=>context.commit('setProductInfo',data))
  }
},

actions中有兩個參數 :

  • context : 與Vuex實體相同的物件,非store本人,但可以透過呼叫他對store進行操作。如果想要某個actions去dispatch另一個actions時,可以透過context.dispatch('...')來完成。
  • payload : 和先前的一樣,外部傳入的值。
export default {
  created() {
    this.$store.dispatch("setProductInfo", { id: "001" });
  },
};

fetchProductInfo在created或mounted階段(也就是實體被建立)被dispatch,會透過API對後端發送邀請,並回傳API結果透過context.commit()提交給setProductInfo。

mapActions

Actions也又自己的mapActions可以用,用下面的寫法也可以得到剛剛的結果。

import { mapActions } from "vuex";

export default {
  methods: {
    ...mapActions(['fetchProductInfo'])
  },
  created() {
    this.fetchProductInfo({ id: "001" });
  },
};

5. modules

在結構圖沒有卻在建立的src/store/index.js中出現的module。他的功能就是管理不斷擴大的state資料。

在modules裡面可以將store拆成兩塊,moduleA和moduleB。

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

export default createStore({
	state:{ ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... },
	modules: {
	    moduleA,
		  moduleB
  }
})

在vue元件時,可以透過mapState取得state.moduleA的資料

computed:{
	...mapState({
		productA:(state)=>store.moduleA.product,
		productB:(state)=>store.moduleB.product
	})
}

rootState

拆成兩個模組後,可以透過rootState取得外層store的資料。

const moduleA= {
	state: ()=>({...}),
  getters: {
    // 需透過第三和四的參數取得rootState、rootGetters
    sumWithRootCount (state, getters, rootState) {
    return state.count + rootState.count
    }
  },
	mutations: {
		//可以透過context存取context.rootState
    increment ({state,commit,rootState}) {
     //...
    }
  },
	actions:{
		//可以透過context存取context.rootState
		incrementIfOddOnRootSum({ state, commit, rootState} = context;
			if((state.count + rootState.count)% 2 === 1){
				commit('increment')
			}
		}
	}
}

namespaced

當模組有相同名稱的actions時,

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: {
		fetchProductInfo(){...}
	},
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: {
		fetchProductInfo(){...}
	},
}

元件下dispatch時兩個會同時被呼叫

this.$store.dispatch('fetchProductInfo')

namespaced可以避免因相同命名而犯的錯誤。

const moduleA = {
	namespaced: true,
  state: () => ({ ... }),
  mutations: { ... },
  actions: {
		fetchProductInfo(){...}
	},
}

const moduleB = {
	namespaced: true,
  state: () => ({ ... }),
  mutations: { ... },
  actions: {
		fetchProductInfo(){...}
	},
}

呼叫的時候就不會找錯人

// moduleA 的 fetchProductInfo 
this.$store.dispatch('moduleA/fetchProductInfo'); 
// moduleB 的 fetchProductInfo 
this.$store.commit('moduleB/fetchProductInfo');

//OR 採用mapActions,mapMutations指定namespaced到第一個參數
methods: { 
	...mapActions('moduleA', ['fetchProductInfo', '...']), 
	...mapMutations('moduleB', ['updateProductInfo', '...']), 
},

模組也有機會dispatch或commit到root,此時只需要加入第三個參數:

dispatch('someOtherActions', null, {root:true})
commit('someMutation', null, {root:true})

參考資料

圖解 Vuex 統一狀態管理
https://chineseplease.moe/wp-content/uploads/2020/04/Vuex.png


上一篇
Day23-Vuex核心概念與結構(part1)
下一篇
Day25-實作
系列文
我的Vue學習筆記30

尚未有邦友留言

立即登入留言