vuex 初體驗

從最簡單的範例開始學習,配合官方網站文件使用

Vuex - passing multiple parameters to action

基本狀態管理

下面範例從官網取得,主要演示了基本的 state 及 mutations 操作

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
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment: state => state.count++,
decrement: state => state.count--
}
});

new Vue({
el: "#app",
store,
computed: {
count() {
return this.$store.state.count;
}
},
methods: {
increment() {
store.commit("increment");
},
decrement() {
store.commit("decrement");
}
}
});
  1. state 用來存放需要被共用的資料,這裡面的資料需要異動的話,需要透過 mutations 來更新資料
  2. store 狀態更新:store.commit('方法名稱',傳入參數)
  3. mutations 第一個參數為 state,第二個參數為傳入的參數,若有多個參數需要傳入,可透過解構來處理
  4. 透過計算屬性取得 store 狀態
  5. 在根組件註冊 store,子組件可透過this.$store訪問到數據

getter 的用法

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.0/vuex.js"></script>
</head>
<body>
<div id="app">
<ul>
<li v-for="(item,index) in list" :key="index">{{ item }}</li>
</ul>
共 {{ listCount }} 筆資料
<button @click="getCountryList">getCountryList</button>
<button @click="getNumberList">getNumberList</button>
</div>

<script>
const ENUMS = {
List: {
Number: "GetNumberList",
Country: "GetCountryList"
}
};

function postData(url, data) {
// Default options are marked with *
return fetch(url, {
body: JSON.stringify(data), // must match 'Content-Type' header
cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
credentials: "same-origin", // include, same-origin, *omit
headers: {
"content-type": "application/json"
},
method: "POST", // *GET, POST, PUT, DELETE, etc.
mode: "cors", // no-cors, cors, *same-origin
redirect: "follow", // manual, *follow, error
referrer: "no-referrer" // *client, no-referrer
}).then(response => response.json()); // 輸出成 json
}

const store = new Vuex.Store({
state: {
list: [],
dataSets:[
paymentInfoDataSet:[]
]
},
actions:{
getPaymentDetail({ commit }, { paymentId, url }) {
postData(url, { paymentId: paymentId })
.then(response => {
commit("updatePaymentInfo", {
paymentId: paymentId,
info: response
});
})
},
},
mutations: {
[ENUMS.List.Number](state, list) {
state.list = [...list];
},
[ENUMS.List.Country](state, list) {
state.list = [...list];
},
["updatePaymentInfo"](state, detail) {
let newDataSet = [...dataSets.paymentInfoDataSet];
let currentData = state.dataSets.paymentInfoDataSet.find(x => x.paymentId === detail.paymentId);
if (currentData) {
let removeIndex = newDataSet.findIndex(function(item) {
return item.paymentId === detail.paymentId;
});
newDataSet.splice(removeIndex, 1);
}
newDataSet.push(detail);
state.dataSets.paymentInfoDataSet = newDataSet;
},
},
getters: {
currentList(state) {
return state.list;
},
currentListCount(state, getters) {
return getters.currentList.length;
}
}
});

new Vue({
el: "#app",
store,
data: {
defList: ["american", "chinese", "japaneses", "europe", "africa"],
numberList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
},
computed: {
list() {
return this.$store.getters.currentList;
},
listCount() {
return this.$store.getters.currentListCount;
}
},
methods: {
getCountryList() {
store.commit(ENUMS.List.Country, this.defList);
},
getNumberList() {
store.commit(ENUMS.List.Number, this.numberList);
}
}
});
</script>
</body>
</html>
  1. getter 大概就相當於是 store 的計算屬性,結果會被 cache 起來,但如果使用方法的格式,有參數傳入,則仍舊是每次呼叫都會執行一次
  2. mutation 做資料更新的話,應該利用新對象替換掉舊對象。例如:state.obj = { ...state.obj, newProp: 123 }
  3. 使用常量替代 Mutation 事件類型,這個可以用也可以不用,但是大專案的話最好還是好好管理一下
  4. mutation 必須是同步函数,非同步的應該使用actions

非同步操作 Actions

Actions 透過 store.dispatch觸發:store.dispatch('getPaymentDetail')
在送出請求並獲得結果後,再呼叫 mutation 更新資料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// actions支持非同步操作
actions: {
checkout ({ commit, state }, products) {
// 把当前购物车的物品备份起来
const savedCartItems = [...state.cart.added]
// 发出结账请求,然后乐观地清空购物车
commit(types.CHECKOUT_REQUEST)
// 购物 API 接受一个成功回调和一个失败回调
shop.buyProducts(
products,
// 成功操作
() => commit(types.CHECKOUT_SUCCESS),
// 失败操作
() => commit(types.CHECKOUT_FAILURE, savedCartItems)
)
}
}

表單處理

若使用狀態管理,在表單處理上若使用 v-model,在嚴格模式下由於資料異動並非是由 mutation 處理,因此會拋出例外
比較簡便的做法是利用 computed 的 get、set,在 set 事件中來處理資料更新

1
<input v-model="message" />
1
2
3
4
5
6
7
8
9
10
computed: {
message: {
get () {
return this.$store.state.obj.message
},
set (value) {
this.$store.commit('updateMessage', value)
}
}
}