[Vue] Vuex 是什麼? 怎麼用? — State、Mutations (1/5)

itsems
itsems
Apr 12 · 12 min read
全系列共五篇:
一、[Vue] Vuex 是什麼? 怎麼用? — State、Mutations (1/5)
二、[Vue] Vuex 是什麼? 怎麼用? — Actions (2/5)
三、[Vue] Vuex 是什麼? 怎麼用? — Getters (3/5)
四、[Vue] Vuex 是什麼? 怎麼用? — Modules (4/5)
五、[Vue] Vuex 是什麼? 怎麼用? — 統整、專案結構(5/5)
Image for post
Image for post

Vuex官網

Outline:
1. Vuex 是什麼?為什麼要用Vuex?
2. 怎麼使用?(State、Mutations)

* 範例中 cli 版本為 cli3,在資料夾結構上 init 出來會跟 cli2 的人不太一樣,不影響使用

Vuex 是什麼?為什麼需要 Vuex?

Image for post
Image for post

所以組件 B 和組件 C 之間的溝通,就會有超多 emitprops ,為了處理大型專案像是這樣跨結構的兄弟組件溝通,而非單純父子組件溝通,Vuex 就產生了。

Vuex 可以做為網站的全域狀態管理,可以將全域狀態集中管理,另一個很像的東西是 Event bus,但是 Event bus 比較像是一個全域的事件,組件 B 可以 emit 送出 bus 的事件或資料,組件 C 則是可以透過 on 來監聽 bus 發出的事件或接收資料。

所以 Event bus 比較適合用在沒有這麼複雜的情況,專案結構比較複雜的時候,則適用 Vuex。

怎麼使用?

Image for post
Image for post

在 Vuex 裡面,儲存狀態的為 State,組件需要更動狀態時,需要透過 Actions 發出一個 Commit 去呼叫 Mutations,再由 Mutations 去更改 State。整個 Vuex 的方法也稱為 store。接下來再來一一說明整個步驟流程。

* 範例是用 vue cli 的方式說明,cdn 的話也可以使用 vuex 的 cdn 引入

1. Install

$ npm i -S vuex

2. Define Vuex

這裡要注意 state 有一點像是 component 的 data,mutate 本身單字的意思是就是「變異」,人如其名就是拿來變動 state 用的。如果 mutations 要做更改,不可以變動在 State 還沒定義的 data

e.g,

如果要在 mutations 定義新的 state,可以像這樣寫:

3. Import

/main.js

import store from './store';new Vue({
store,
render: h => h(App),
}).$mount('#app')

4. Usage in component

<template>
<div>
<p>Loading: {{ifLoading}}</p>
<button @click="reverseLoad()">Reverse</button>
</div>
</template>

在 script 裡面:

template 裡面的 ifLoading 是 computed 裡面的 ifLoading 函式,會 return state 的 isLoading 狀態。button 的 click 事件 reverseLoad 則在 methods 裡面會呼叫 store commit Loaded 這個 mutations

Image for post
Image for post

簡單的 state 差不多就這樣了,

但是我如果有不只一個 state 呢?如果我還要看我點了幾次這個 button 呢?

以上面的做法就會變成:

在 store.js 再新增一個 state 跟 mutations :

/store.js

const store = new Vuex.Store({
state: {
isLoading: false,
clickedTimes: 0,
},
mutations: {
Loaded(state) {
state.isLoading = !state.isLoading;
},
addTimes(state) {
state.clickedTimes += 1;
},
}
})

/App.vue

template

<button @click="reverseLoad();addTimes()">Reverse</button>
<p>Button Clicked Times: {{clicked}}</p>

script

computed: {
ifLoading() {
return this.$store.state.isLoading;
},
clicked() {
// 抓 state 的 clickedTimes
return this.$store.state.clickedTimes;
}
},
methods: {
reverseLoad() {
this.$store.commit("Loaded");
},
addTimes() {
// commit 執行 addTimes 這個 mutations
this.$store.commit("addTimes");
}
}

很冗,如果再多幾個,就會開始很醜。

怎麼辦,不要怕,有解,Vuex 提供了一個叫做 mapState 的這個方法, mapState 有兩種方法可以使用:

  1. 陣列指定

這邊陣列裡面的字串 isLoading,就是 store 裡面的 state,所以 template 不能像剛剛那樣自己設不一樣的名字,要設定 store 裡面 state 的名稱

原本 computed 是一個物件,但是 mapState 也會回傳物件,所以可以直接這樣寫

2. 物件指定

如果你還是想要在 template 上設定一個不一樣的名字,就可以用物件的方式使用:

這裡的 isLoading 的地方除了是字串之外,也可以寫成函式:

computed: mapState({
ifLoading(state) {
return state.isLoading;
},
Times(state) {
return state.clickedTimes;
}

}),

因為沒有 this,也只有 state 一個參數,可以簡化為箭頭函式 (ES6):

computed: mapState({
ifLoading: state => state.isLoading,
Times: state => state.clickedTimes

}),

通常我們的 computed 裡面,不會只有 mapState,也會有別的 computed 要使用,可以使用 ES6 的 來達成:

computed: {
otherfn() {
return "asdf";
},
...mapState({
ifLoading: state => state.isLoading,
Times: state => state.clickedTimes
})
},

多個 mutations 也有 mapMutations 可以同理使用:

Image for post
Image for post

這樣就處理了多個 state 和 mutations 的問題了

另外 mutations 還有一個重點: payload

如果我今天想要點一次按鈕就加 2 次而不是加 1 次呢? 加 n 次呢?

在 store.js 中,先在 mutations 加上一個 payload 參數:

mutations: {
addTimes(state, payload) {
state.clickedTimes = state.clickedTimes + payload;
},
}

回到 App.vue,我們先把剛剛轉為 mapMutations 的 methods 轉換回來,然後在 commit 後面帶入要帶入的 payload

/App.vue

<template>
<div id="app">
<p>Loading: {{ifLoading}}</p>
<button @click="reverseLoad();add()">Reverse</button>
<p>Button Clicked Times: {{Times}}</p>
</div>
</template>
<script>
...,
computed: {
...
},
methods: {
add() {
return this.$store.commit("addTimes", 2);
},

}
};
</script>

commit 這邊原本只有 addTimes 這個字串,代表 store 的 mutations,帶入的第二個參數,會當作是 payload,commit 裡面也可以帶入物件:

addTimes() {
return this.$store.commit({
type: "addTimes",
count: 2
}
);
}

type 是必要的,表示 mutations 的名字,其他的都可以自定義,假設在這邊設定了一個 key 是 count,想要按鈕的次數是加上這個 count,在 store.js 裡面就要再加上:

addTimes(state, payload) {
state.clickedTimes = state.clickedTimes + payload.count;
},

就可以抓到在物件裡面設定的值了

一樣使用 mapMutations 的話,就把 payload 值直接在 template 使用時傳入,就可以了:

<button @click="Loaded();addTimes(2)">Reverse</button>
Image for post
Image for post

這樣就完成 mutations 的 payload 設定了 🏊🏊🏊

統整一下 State 和 Mutations 的寫法:

single State :

ifLoading() {
return this.$store.state.isLoading;
},

ifLoading 為 template 自定義,isLoading 則為 store 的 state 定義的

mapState:

一、陣列指定

...mapState(["isLoading", "clickedTimes"])

這邊的字串同等於 store.js 裡面的 state 名稱,上面的 template 也要取同名

二、物件指定

...mapState({
ifLoading: state => state.isLoading,
})

ifLoading 可為 template 自定義,isLoading 則為 store.js 定義的 state

2. 字串

...mapState({
Times: "clickedTimes",
})

Times 為 template 自定義,clickedTimes 為 store.js 定義的 state

3. 函式

data() {
return {
name: "emma"
};
},
computed: {
...mapState({
myName(state) {
return state.myName + this.name;
}
})
},

這邊的 this.name 就是 component 的 this,就可以跟 component 的資料一起處理

single Mutations :

commit 函式

reverseLoaded() {
this.$store.commit("Loaded");
},
// String with payload
add() {
this.$store.commit("addTimes", 2);
}
// Object with payload
add() {
this.$store.commit({ type: "addTimes", key: 2 });
}

用物件傳送 object 的話,type 為必填,名字是要觸發的 mutations,其他的 key 都可以,但是在這邊定義了要加上的 payload 是 2,則 store.js 裡面的 addTimes mutations 要加上 state.clickedTimes = state.clickedTimes + payload.key;

mapMutations:

...mapMutations(["Loaded", "addTimes"])

在這邊的如果需要加上 payload,就要加在 template 上面: addTimes(2)

2. 物件

...mapMutations({
reverse: "Loaded",
add: "addTimes"
})

payload 一樣需要加在 template 上,reverseadd 則可由 template 定義

Mutations 只能做同步的操作,不可以做非同步的操作,如果需要非同步的操作,可以用 Actions,Actions 下篇見~

itsems_frontend

Front-end in Taiwan, Taipei

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store