1. Vuex簡(jiǎn)介
Vuex是專門(mén)用來(lái)管理vue.js應(yīng)用程序中狀態(tài)的一個(gè)插件只冻。他的作用是將應(yīng)用中的所有狀態(tài)都放在一起朴译,集中式來(lái)管理。需要聲明的是莫湘,這里所說(shuō)的狀態(tài)指的是vue組件中data里面的屬性倾鲫。
2. vuex的組成結(jié)構(gòu)示意圖
3. vuex 的核心概念
- 一個(gè)完整的store的結(jié)構(gòu)是這樣的
const store = new Vuex.Store({
state: {
// 存放狀態(tài)
},
getters: {
// state的計(jì)算屬性
},
mutations: {
// 更改state中狀態(tài)的邏輯粗合,同步操作
},
actions: {
// 提交mutation,異步操作
},
// 如果將store分成一個(gè)個(gè)的模塊的話乌昔,則需要用到modules隙疚。
//然后在每一個(gè)module中寫(xiě)state, getters, mutations, actions等。
modules: {
a: moduleA,
b: moduleB,
// ...
}
});
vuex 中最關(guān)鍵的是store對(duì)象磕道,這是vuex的核心供屉。可以說(shuō),vuex這個(gè)插件其實(shí)就是一個(gè)store對(duì)象伶丐,每個(gè)vue應(yīng)用僅且僅有一個(gè)store對(duì)象悼做。
store是Vuex.Store這個(gè)構(gòu)造函數(shù)new出來(lái)的實(shí)例。在構(gòu)造函數(shù)中可以傳一個(gè)對(duì)象參數(shù)哗魂。這個(gè)參數(shù)中可以包含5個(gè)對(duì)象:
1.state – 存放狀態(tài)
2.getters – state的計(jì)算屬性
3.mutations – 更改狀態(tài)的邏輯肛走,同步操作
4.actions – 提交mutation,異步操作
5.mudules – 將store模塊化
- 關(guān)于store录别,需要先記住兩點(diǎn):
store 中存儲(chǔ)的狀態(tài)是響應(yīng)式的朽色,當(dāng)組件從store中讀取狀態(tài)時(shí),如果store中的狀態(tài)發(fā)生了改變组题,那么相應(yīng)的組件也會(huì)得到更新葫男;
不能直接改變store中的狀態(tài)。改變store中的狀態(tài)的唯一途徑是提交(commit)mutations崔列。這樣使得我們可以方便地跟蹤每一個(gè)狀態(tài)的變化梢褐。
3.1 state
state上存放的,說(shuō)的簡(jiǎn)單一些就是變量赵讯,也就是所謂的狀態(tài)盈咳。沒(méi)有使用 state 的時(shí)候,我們都是直接在 data 中進(jìn)行初始化的瘦癌,但是有了 state 之后猪贪,我們就把 data 上的數(shù)據(jù)轉(zhuǎn)移到 state 上去了跷敬。另外有些狀態(tài)是組件私有的狀態(tài)讯私,稱為組件的局部狀態(tài),我們不需要把這部分狀態(tài)放在store中去西傀。
3.2.1 如何在組件中獲取vuex狀態(tài)
由于vuex的狀態(tài)是響應(yīng)式的斤寇,所以從store中讀取狀態(tài)的的方法是在組件的計(jì)算屬性中返回某個(gè)狀態(tài)。
import store from 'store';
const Counter = {
template: <div>{{ count }}</div>
,
computed: {
count () {
// 獲取store中的狀態(tài)
return store.state.count;
}
}
}
這樣拥褂,組件中的狀態(tài)就與store中的狀態(tài)關(guān)聯(lián)起來(lái)了娘锁。每當(dāng)store.state.count發(fā)生變化時(shí),都會(huì)重新求取計(jì)算屬性饺鹃,從而更新DOM莫秆。
然而,每個(gè)組件中都需要反復(fù)倒入store悔详∧魇海可以將store注入到vue實(shí)例對(duì)象中去,這樣每一個(gè)子組件中都可以直接獲取store中的狀態(tài)茄螃,而不需要反復(fù)的倒入store了缝驳。
const app = new Vue({
el: '#app',
// 把 store 對(duì)象注入到了
store,
components: { Counter },
template: <div> <counter></counter> </div>
});
這樣可以在子組件中使用this.$store.state.count訪問(wèn)到state里面的count這個(gè)狀態(tài)
const Counter = {
template: <div>{{ count }}</div>
,
computed: {
count () {
// 獲取store中的狀態(tài)
return this.$store.state.count;
}
}
}
3.2.2 mapState
當(dāng)一個(gè)組件獲取多種狀態(tài)的時(shí)候,則在計(jì)算屬性中要寫(xiě)多個(gè)函數(shù)。為了方便用狱,可以使用mapState輔助函數(shù)來(lái)幫我們生成計(jì)算屬性运怖。
import { mapState } from 'vuex';
export default {
// ...
data (){
localState: 1
}
computed: mapState({
// 此處的state即為store里面的state
count: state => state.count,
// 當(dāng)計(jì)算屬性的名稱與state的狀態(tài)名稱一樣時(shí),可以省寫(xiě)
// 映射 this.count1 為 store.state.count1
count1,
//'count'等同于 ‘state => state.count’
countAlias: 'count',
countPlus (state){
// 使用普通函數(shù)是為了保證this指向組件對(duì)象
return state.count + this.localState;
}
})
}
//上面是通過(guò)mapState的對(duì)象來(lái)賦值的夏伊,還可以通過(guò)mapState的數(shù)組來(lái)賦值
computed: mapState(['count']);
//這種方式很簡(jiǎn)潔摇展,但是組件中的state的名稱就跟store中映射過(guò)來(lái)的同名
對(duì)象擴(kuò)展運(yùn)算符
mapState 函數(shù)返回的是一個(gè)對(duì)象,為了將它里面的計(jì)算屬性與組件本身的局部計(jì)算屬性組合起來(lái)署海,需要用到對(duì)象擴(kuò)展運(yùn)算符吗购。
computed: {
localState () {
...mapState ({
})
}
這樣,mapState中的計(jì)算屬性就與localState計(jì)算屬性混合一起了砸狞。
3.3 getters
有時(shí)候我們需要從 store 中的 state 中派生出一些狀態(tài)捻勉,例如對(duì)列表進(jìn)行過(guò)濾并計(jì)數(shù)。此時(shí)可以用到getters刀森,getters可以看作是store的計(jì)算屬性踱启,其參數(shù)為state。
const store = new Vuex.Store({
state: {
todos: [
{id: 1, text: 'reading', done: true},
{id: 2, text: 'playBastketball', done: false}
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done);
}
}
});
3.3.1 獲取getters里面的狀態(tài)研底,方法一
store.getters.doneTodos // [{ id: 1, text: 'reading', done: true }]
//在組件中埠偿,則要寫(xiě)在計(jì)算屬性中,
computed: {
doneTodos () {
return this.$store.getters.doneTodos;
}
}
3.3.2 使用mapGetters獲取getters里面的狀態(tài):方法二
import {mapState, mapGetters} from 'vuex';
computed: {
...mapState(['increment']),
...mapGetters(['doneTodos'])
}
3.4 mutations
mutations里面是如何更改state中狀態(tài)的邏輯榜晦。更改Vuex中的state的唯一方法是冠蒋,提交mutation,即store.commit(‘increment’)乾胶。
3.4.1 提交載荷(payload)
可以向commit傳入額外的參數(shù)抖剿,即mutation的載荷。
mutations: {
increment(state, n){
state.count += n;
}
}
store.commit('increment', 10);
payload還可以是一個(gè)對(duì)象识窿。
mutations: {
increment(state, payload)
state.count += payload.amount;
}
}
store.commit('increment', {amount: 10});
還可以使用type屬性來(lái)提交mutation斩郎。
store.commit({
type: 'increment',
amount: 10
});
// mutations保持不變
mutations: {
increment(state, payload){
state.count += payload.amount;
}
}
注意:mutation必須是同步函數(shù),不能是異步的喻频,這是為了調(diào)試的方便缩宜。
3.4.2 在組件中提交mutations
那么mutation應(yīng)該在哪里提交呢? 因?yàn)閖s是基于事件驅(qū)動(dòng)的甥温,所以改變狀態(tài)的邏輯肯定是由事件來(lái)驅(qū)動(dòng)的锻煌,所以store.commit(‘increment’)是在組件的methods中來(lái)執(zhí)行的。
方法1: 在組件的methods中提交
methods: {
increment(){
this.$store.commit('increment');
}
}
方法2: 使用mapMutaions
用 mapMutations 輔助函數(shù)將組件中的 methods 映射為 store.commit 調(diào)用姻蚓。
import { mapMutaions } from 'vuex';
export default {
methods: {
...mapMutaions([
'increment' // 映射 this.increment() 為 this.store.commit('increment')
])
}
}
// 因?yàn)閙utation相當(dāng)于一個(gè)method宋梧,所以在組件中,可以這樣來(lái)使用
<button @click="increment">+</button>
3.5 actions
因?yàn)閙utations中只能是同步操作史简,但是在實(shí)際的項(xiàng)目中乃秀,會(huì)有異步操作肛著,那么actions就是為了異步操作而設(shè)置的。這樣跺讯,就變成了在action中去提交mutation枢贿,然后在組件的methods中去提交action。只是提交actions的時(shí)候使用的是dispatch函數(shù)刀脏,而mutations則是用commit函數(shù)局荚。
3.5.1 一個(gè)簡(jiǎn)單的action
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state){
state.count++;
}
},
actions: {
increment(context){
context.commit('increment');
}
/* 可以用參數(shù)結(jié)構(gòu)的方法來(lái)寫(xiě)action
increment({commit}){
commit('increment');
}
*/
}
});
// action函數(shù)接受一個(gè)context參數(shù),這個(gè)context具有與store實(shí)例相同的方法和屬性愈污。
// 分發(fā)action
store.dispatch('increment');
action同樣支持payload和對(duì)象方式來(lái)分發(fā)碟绑,格式跟commit是一樣的难裆,不再贅述。
3.5.2 在組件中分發(fā)action
方法1: 在組件的methods中,使用this.$store.dispatch(‘increment’)圈盔。
方法2: 使用mapActions授瘦,跟mapMutations是類似的二拐。
import { mapActions } from 'vuex'
export default {
methods: {
...mapActions([
'increment' // 映射 this.increment() 為 this.store.dispatch('increment')
})
}
}
// 同樣在組件中茂翔,可以這樣來(lái)使用
<button @click="increment">+</button>
3.5.3 組合actions
因?yàn)閍ction是異步的,那么我們需要知道這個(gè)異步函數(shù)什么時(shí)候結(jié)束涧尿,以及等到其執(zhí)行后系奉,會(huì)利用某個(gè)action的結(jié)果。這個(gè)可以使用promise來(lái)實(shí)現(xiàn)姑廉。在一個(gè)action中返回一個(gè)promise缺亮,然后使用then()回調(diào)函數(shù)來(lái)處理這個(gè)action返回的結(jié)果。
actions:{
actionA({commit}){
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation');
resolve();
},1000);
})
}
}
// 這樣就可以操作actionA返回的結(jié)果了
store.dispatch('actionA').then(() => {
// dosomething ...
});
// 也可以在另一個(gè)action中使用actionA的結(jié)果
actions: {
actionB({ dispatch, commit }){
return dispatch('actionA').then(() => {
commit('someOtherMutation');
})
}
}
4 mudules
module是為了將store拆分后的一個(gè)個(gè)小模塊桥言,這么做的目的是因?yàn)楫?dāng)store很大的時(shí)候萌踱,分成模塊的話,方便管理限书。
4.1 每個(gè)module擁有自己的state, getters, mutation, action
const moduleA = {
state: {...},
getters: {...},
mutations: {....},
actions: {...}
}
const moduleB = {
state: {...},
getters: {...},
mutations: {....},
actions: {...}
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
});
store.state.a // 獲取moduleA的狀態(tài)
store.state.b // 獲取moduleB的狀態(tài)
4.2 模塊內(nèi)部的狀態(tài)
對(duì)于模塊內(nèi)部的mutation和getter虫蝶,接受的第一個(gè)參數(shù)是模塊的局部狀態(tài)state章咧。順便說(shuō)一下倦西,根結(jié)點(diǎn)的狀態(tài)為rootState。
const moduleA = {
state: { count: 0},
getters: {
doubleCount(state){
return state.count * 2;
}
},
mutations: {
increment(state){
state.count ++ ;
}
},
actions: {...}
}
4.3 模塊的動(dòng)態(tài)注冊(cè)
在模塊創(chuàng)建之后赁严,可以使用store.registerModule方法來(lái)注冊(cè)模塊扰柠。
store.registerModule('myModule', {
// ...
});
依然的,可以通過(guò)store.state.myModule來(lái)獲取模塊的狀態(tài)疼约。
可以使用store.unregisterModule(moduleName)來(lái)動(dòng)態(tài)的卸載模塊卤档,但是這種方法對(duì)于靜態(tài)模塊是無(wú)效的(即在創(chuàng)建store時(shí)聲明的模塊)。
5 含有vuex的項(xiàng)目的結(jié)構(gòu)
5.1 應(yīng)該遵循的規(guī)則
應(yīng)用層級(jí)的狀態(tài)都應(yīng)該集中在store中
提交 mutation 是更改狀態(tài)state的唯一方式程剥,并且這個(gè)過(guò)程是同步的劝枣。
異步的操作應(yīng)該都放在action里面