本篇紀錄專案中遇到的情境,剛好練習一下 Getters 的用法。
此需求情境為當指定條件下則不要顯示折扣優惠的結果,此需求在專案中的三個頁面會出現此狀態。
首先,有個下拉選單去選取到票卡選項,並且按下送出時,其他頁面要呈現是否有選到票卡的狀態,類似下方程式碼:
chooseCard.vue
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| <template> <div> <input v-model="selectedCard" type="text" />
<select> <option v-for="item in options" :key="item.label" :value="item.value" @change="(event) => (selectedCard = event.target.value)" > {{item.label}} </option> </select>
<button @click="apply()">click</button>
// 此區顯示是否折扣優惠 <div v-if="isShowCobrandCard">Discounts</div> </div> </template>
<script> import { mapGetters } from "vuex"; export default { data() { return { selectedCard: "", options: [ { label: "test01", value: "test01", }, { label: "test02", value: "test02", }, { label: "test03", value: "test03", }, ], }; }, computed: { ...mapGetters("ticket", ["getCobrandCard"]), isShowCobrandCard() { const excludedCodes = ["A1", "A2", "A3"]; const getTicketCode = this.vxData.map((item) => item.ticketCode.substring(0, 2) ); const hasNoExcludedCode = getTicketCode.some( (code) => !excludedCodes.includes(code) ); return hasNoExcludedCode; }, }, methods: { apply() { this.$router.push("/next-page"); }, }, }; </script>
|
多頁使用同個方法
寫完基本功能後,發現有三頁有使用到此方法,
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| computed: { ...mapGetters('ticket', ['getCobrandCard']), isShowCobrandCard() { const excludedCodes = ['A1', 'A2', 'A3']; const getTicketCode = this.vxData.map((item) => item.ticketCode.substring(0, 2), ); const hasNoExcludedCode = getTicketCode.some( (code) => !excludedCodes.includes(code), ); return hasNoExcludedCode; }, },
|
原本的想法
一開始想說儲存在 state
中,並下拉選單有變更時,再透過 mutations
的方法寫入,後來改成這樣。
chooseCard.vue
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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| <template> <div> <input v-model="selectedCard" type="text" />
<select> <option v-for="item in options" :key="item.label" :value="item.value" @change="getCardInfo($event)" //改寫 change > {{item.label}}</option > </select>
<button @click="apply()">click</button>
// 此區顯示是否折扣優惠 <div v-if="isShowCobrandCard">Discounts</div> </div> </template>
<script> import { mapGetters, mapMutations } from 'vuex'; export default { data() { return { ...略... }; }, computed: { ...mapGetters('ticket', ['getCobrandCard']), ...mapMutations('ticket', ['setHasGetCardInfo']), isShowCobrandCard() { const excludedCodes = ['A1', 'A2', 'A3']; const getTicketCode = this.vxData.map((item) => item.ticketCode.substring(0, 2), ); const hasNoExcludedCode = getTicketCode.some( (code) => !excludedCodes.includes(code), ); return hasNoExcludedCode; }, }, methods: { apply() { this.$router.push('/next-page'); }, getCardInfo(e) { this.selectedCard = e.target.value; this.setHasGetCardInfo(e.target.value); }, }, }; </script>
|
調整過程
經過跟資深工程師討論後,以操作介面來說可以在按下 apply()
按鈕時再取得卡片的資訊,不然每選一次就更新 state
,似乎有點多餘,深感認同。後來就改成這樣。
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 35 36 37 38 39 40 41 42 43 44
| <template> <div> <input v-model="selectedCard" type="text" />
<select> <option v-for="item in options" :key="item.label" :value="item.value" @change="($event) => (selectedCard = $event.target.value)" // 改回原本寫法 > {{item.label}}</option > </select>
<button @click="apply()">click</button> </div> </template>
<script> import { mapGetters, mapMutations } from 'vuex'; export default { data() { return { }; }, computed: { ...mapGetters('ticket', ['getCobrandCard']), ...mapMutations('ticket', ['setHasGetCardInfo']), isShowCobrandCard() { }, } methods: { apply() { this.$router.push('/next-page'); this.setHasGetCardInfo(this.selectedCard); }, }, } </script>
|
此時僅保留 mutations 存取資料的部分。
使用 Getters 改寫 computed 共同邏輯
因為這三個頁面的 computed
幾乎都用的 isShowCobrandCard()
取到值,因為專案有使用 Vuex,故資深工程師建議我可以改寫成 Getters,就可以一起共用,也不需要特別把取得卡片的資料寫在 state
儲存,因為是否選到票卡的資料也是直接從 state 某個變數取得的,可以直接從 Getters
取到資料內一起完成。
這樣連 state
跟 mutations
也都不需要了。
把 computed 改寫成 Getters
後來將共用的 computed
改寫在 Getters
,程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| vxIsShowCardInfo(state) { const { resultDataList } = state; const excludedCodes = ['A1', 'A2', 'A3']; const hasSelectedCard = resultDataList?.every( ({ selected }) => selected.option.price.corporate, ); const ticketCodes = resultDataList?.map( ({ selected }) => selected.option.ticketCode.substring(0, 2) || [], ); const hasNoExcludedCode = ticketCodes.some( (code) => !excludedCodes.includes(code), ); return hasSelectedCard && hasNoExcludedCode; },
|
Getters 類似 Vue 的 computed
,但不同的是,Vue 的 computed
不能傳入參數,但 Getters 是可以帶入參數的,通常第一個會是 state
,所以就從這邊取得資料直接做處理。
說明:
- 使用 ES6 的方始取得
state
的變數 resultDataList
。
- 並且把本次需求,若在指定條件下,陣列中有包含這三個值中的兩個,就不要顯示卡片資訊。
hasSelectedCard
是判斷是否有使用卡片,因為指定條件有單程跟來回,所以使用 every()
去找所有的物件是否有包含 corporate
。若沒有使用卡片,物件則不會出現此屬性。
ticketCodes
是把資料重新 map() 成兩筆物件,並找出 ticketCode
只取前兩個字串。
hasNoExcludedCode
是判斷 ticketCodes
裡面是否有包含所指定的代碼,會回傳 true 或 false。
- 最後要把 true 或 false 的結果回傳給
vxIsShowCardInfo
,並替換掉原本的 isShowCobrandCard
的地方。
這樣原本有共同使用到這片段程式碼的地方,只要透過 mapGetters,就可以使用這個取值方法。這三頁的程式碼就可以精簡很多,而且吃到的都是同一個邏輯。感謝同事的提點。
小結
這次優化這個功能遇到了幾個需要學習的地方:
- 對於 Getters 沒有這麼熟悉,透過這次練習比較知道 Getters 的使用。
- 如果是處理共同資料流的地方,可以先想到 Getters。
- 研究流程與取值之間有沒有更好的做法。
- 從功能開發到優化的學習,真心感謝資深工程師的提點,有人願意提點該高興,被放生就慘了。
- 寫註解要清楚明瞭,至少讓自己在數週或數個月後還可以知道自己在寫什麼。
- Git 協作觀念還要加強,解衝突不要亂解。
參考資料