0%

Vue 筆記 - 將不同頁面的 computed 透過 Getter 整合成共用方法,以及優化過程

getter

本篇紀錄專案中遇到的情境,剛好練習一下 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"]),
// 是否顯示 icon
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() {
// ...do something
this.$router.push("/next-page");
},
},
};
</script>

多頁使用同個方法

寫完基本功能後,發現有三頁有使用到此方法,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
computed: {
...mapGetters('ticket', ['getCobrandCard']),
// 是否顯示 icon
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']),
// 是否顯示 icon
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() {
// ...do something
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']),
// 是否顯示 icon
isShowCobrandCard() {
// ...略...
},
}
methods: {
apply() {
// ...do something
this.$router.push('/next-page');
this.setHasGetCardInfo(this.selectedCard); // 改到這裡
},
},
}
</script>

此時僅保留 mutations 存取資料的部分。

使用 Getters 改寫 computed 共同邏輯

因為這三個頁面的 computed 幾乎都用的 isShowCobrandCard() 取到值,因為專案有使用 Vuex,故資深工程師建議我可以改寫成 Getters,就可以一起共用,也不需要特別把取得卡片的資料寫在 state 儲存,因為是否選到票卡的資料也是直接從 state 某個變數取得的,可以直接從 Getters 取到資料內一起完成。

這樣連 statemutations 也都不需要了。

把 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,
);
// 取得票卡前兩碼,ex.A1, A2, A3
const ticketCodes = resultDataList?.map(
({ selected }) => selected.option.ticketCode.substring(0, 2) || [],
);
// 判斷票卡前兩碼是否有包含 A1, A2, A3
const hasNoExcludedCode = ticketCodes.some(
(code) => !excludedCodes.includes(code),
);
// 用票卡購買來回皆不包含 [A1,A2,A3] 才顯示卡片相關資訊。
return hasSelectedCard && hasNoExcludedCode; // ture or false
},

Getters 類似 Vue 的 computed,但不同的是,Vue 的 computed 不能傳入參數,但 Getters 是可以帶入參數的,通常第一個會是 state,所以就從這邊取得資料直接做處理。

說明:

  1. 使用 ES6 的方始取得 state 的變數 resultDataList
  2. 並且把本次需求,若在指定條件下,陣列中有包含這三個值中的兩個,就不要顯示卡片資訊。
  3. hasSelectedCard 是判斷是否有使用卡片,因為指定條件有單程跟來回,所以使用 every() 去找所有的物件是否有包含 corporate。若沒有使用卡片,物件則不會出現此屬性。
  4. ticketCodes 是把資料重新 map() 成兩筆物件,並找出 ticketCode 只取前兩個字串。
  5. hasNoExcludedCode 是判斷 ticketCodes 裡面是否有包含所指定的代碼,會回傳 true 或 false。
  6. 最後要把 true 或 false 的結果回傳給 vxIsShowCardInfo,並替換掉原本的 isShowCobrandCard 的地方。

這樣原本有共同使用到這片段程式碼的地方,只要透過 mapGetters,就可以使用這個取值方法。這三頁的程式碼就可以精簡很多,而且吃到的都是同一個邏輯。感謝同事的提點。

小結

這次優化這個功能遇到了幾個需要學習的地方:

  1. 對於 Getters 沒有這麼熟悉,透過這次練習比較知道 Getters 的使用。
  2. 如果是處理共同資料流的地方,可以先想到 Getters。
  3. 研究流程與取值之間有沒有更好的做法。
  4. 從功能開發到優化的學習,真心感謝資深工程師的提點,有人願意提點該高興,被放生就慘了。
  5. 寫註解要清楚明瞭,至少讓自己在數週或數個月後還可以知道自己在寫什麼。
  6. Git 協作觀念還要加強,解衝突不要亂解。

參考資料