Getting Started with Vuex
Vue:2.6
Vuex:3.6.2
Vuex 是專門為 Vue.js
開發的 狀態管理模式(State Management Pattern and Library)
,它會集中式儲存並管理應用所有元件的狀態,並且以相對應的規則去操作狀態。再更簡單一點來說,就是建立一個單向的資料流,讓狀態變化是可預測的。
舉例來說,以電商的購物車功能為例,各個頁面都需要被共享到目前購物車的細節,這時候就可以透過 Vuex
來實作共享狀態。
另外, Vuex
跟 Event bus
差別是,Event bus
是類似全域事件的溝通,可以透過 props
和 emit
進行資料的傳遞,也就是說,這些事件是需要透過元件間的觸發才能運作;而 Vuex
比較接近是狀態的共享和管理,讓各個元件之間可以同步一些全域的狀態。
Lifecycle
(圖片來源:itread01)
在 Vuex
的 Lifecycle 裡有三個重要的機制需要知道。
State
: 儲存狀態的物件,使用單一狀態樹(Single State Tree)
的方式來存放。可以透過Vuex Getters
取得狀態。Mutations
:Vuex
中改變狀態的唯一方法,通常都是用來實作狀態最原始的更動。Actions
:可以用來呼叫Mutations
的方法,可以支援非同步呼叫(Asynchronous),通常用來實作商業邏輯。
Installation
透過 npm
進行安裝便可以直接使用。
npm install vuex --save
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
另外,因為 Vuex
依賴 Promise,所以 IE 瀏覽器
下是跑不動的,我們需要再安裝 es6-promise 套件並引用。
npm install es6-promise --save
import 'es6-promise/auto';
Basic Usage
Vuex
是用 Store
來當作儲存狀態的單位,因此一開始我們可以建立一個 store
的目錄開始實作。
src/store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++;
}
},
actions: {
// 傳入一個 Store instance
increment (context) {
// 呼叫 mutations.increment
context.commit('increment');
}
},
});
export default store;
接著在主程式引用 store
。
src/app.js
import Vue from 'vue';
export default new Vue({
el: '#app',
router,
store
});
最後就可以在 Components
裡面取得相關資料啦。
Components
<template>
<div class="home">
<button @click="incrementByCommit()">+1 by commit</button>
<button @click="incrementByDispatch()">+1 by dispatch</button>
</div>
</template>
<script>
export default {
mounted() {
// 可以透過 this.$store.state 取得 state
console.log(this.$store.state.count);
},
methods: {
incrementByCommit() {
// 透過提交 Mutation 的方式更改狀態
this.$store.commit('increment');
console.log(this.$store.state.count);
},
incrementByDispatch() {
// 透過呼叫 Action 的方式更改狀態
this.$store.dispatch('increment');
console.log(this.$store.state.count);
}
}
}
</script>
Getters
Vuex
提供的 Getters
功能,類似元件中的 Computed
機制,每當狀態改變時才會進行計算。這樣一來各個元件在使用特定狀態時,就不需要每一個元件都需要寫一樣的 Computed
,只要在 Store
裡實作 Getters
就好了。
比方說,我們可以透過 Getters
實作找出女性會員的資訊。
src/store/index.js
const store = new Vuex.Store({
state: {
members: [
{ id: 1, name: 'JohnsonLu', gender: 'male' },
{ id: 2, name: 'Tina', gender: 'female' },
{ id: 3, name: 'Yolanda', gender: 'female' },
]
},
getters: {
females: state => {
return state.members.filter(members => members.gender == 'female');
},
// 也可把其他 getters 當成參數使用
memberCount: (state, getters)=> {
return getters.females.length;
}
}
});
Components
export default {
mounted() {
// [{ id: 2, name: 'Tina', gender: 'female' }, { id: 3, name: 'Yolanda', gender: 'female' }]
console.log(this.females);
},
computed: {
females () {
return this.$store.getters.females;
}
}
}
另外,可以透過 mapGetters
來取得 Getters
,可以簡化程式碼。
Components
import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters([
'females',
'memberCount'
]),
}
}
Vuex
中,分別有 mapState
、mapMutations
、mapActions
、mapGetters
可以使用。
Modules
在大型專案裡,Store
可能會變得很肥,因此 Vuex
也支援 Modules
功能,可以將 Store
拆成數個 Modules
,讓程式比較好維護。
src/store/index.js
// Module A
const moduleA = {
state: {
count: 0
},
mutations: {},
actions: {},
getters: {}
}
// Module B
const moduleB = {
state: {
name: 'JohnsonLu'
},
mutations: {},
actions: {}
}
const store = new Vuex.Store({
// 引入 Modules
modules: {
a: moduleA,
b: moduleB
}
});
之後便可以透過 store.state.a.count
和 store.state.b.name
取得相對應的值。
正常狀況下,Store
裡的資訊都是屬於 global namespace 的,大家共用一樣的 Actions/Mutations
。但如果想要有更高度的封裝性和結構的話,也可以開啟 Namespacing
功能,讓狀態們同時獲得 Namespace
。
src/store/index.js
const store = new Vuex.Store({
// 引入 Modules
modules: {
// Person Module
person: {
// 開啟 Namespacing
namespaced: true,
state: { age: 18 }, // state.person.age
// 引入子 Modules
modules: {
company: {
state: { title: 'Engineer' } // state.person.company.title
}
},
}
}
});
關於 Modules
還有許多更細節的操作,可以再參考 Vuex Modules 的說明。