什么是Vuex及Vuex的優(yōu)缺點(diǎn)
Vuex是專為Vue.js應(yīng)用程序開發(fā)的狀態(tài)管理模式阿弃。比如一個(gè)簡(jiǎn)單的Vue計(jì)數(shù)應(yīng)用:
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
這個(gè)狀態(tài)自管理應(yīng)用包含以下三部分:
· state贴唇,驅(qū)動(dòng)應(yīng)用的數(shù)據(jù)源
· view,以聲明方式將state映射到視圖
· actions豌拙,響應(yīng)在view上的用戶輸入導(dǎo)致的狀態(tài)變化
對(duì)于如下圖所示的單向數(shù)據(jù)流:
當(dāng)遇到多個(gè)組件共享狀態(tài)的時(shí)候,其簡(jiǎn)潔性很容易被破壞:
· 多個(gè)視圖依賴于同一狀態(tài)
· 來自不同視圖的行為需要變更同一狀態(tài)
所以,可以把組件的共享狀態(tài)抽取出來胡嘿,以一個(gè)全局單例模式管理,在這種模式下钳踊,組件樹構(gòu)成了一個(gè)巨大的“視圖”衷敌,不管在樹的哪個(gè)位置,任何組件都能獲取狀態(tài)或觸發(fā)行為拓瞪。Vuex便由此而生缴罗。
Vuex的優(yōu)缺點(diǎn)
Vuex可以幫助管理共享狀態(tài),對(duì)開發(fā)大型單頁應(yīng)用帶來了方便祭埂。但如果要開發(fā)的應(yīng)用足夠簡(jiǎn)單面氓,Vuex可能就顯得相對(duì)繁瑣冗余,不適合使用。
Vuex五大屬性
Vuex的五大屬性分別是:state侧但、getter矢空、mutation、action禀横、module
根據(jù)我個(gè)人的理解屁药,可以把Vuex與Vue組件進(jìn)行對(duì)照記憶:
**state => 基本數(shù)據(jù) => data
getters => 從基本數(shù)據(jù)派生的數(shù)據(jù) => computed
mutations => 提交更改數(shù)據(jù)的方法,同步柏锄! => 同步methods
actions => 像一個(gè)裝飾器酿箭,包裹mutations,使之可以異步趾娃。 => 異步調(diào)用methods
modules => 模塊化Vuex
1. state
2. getter
3. mutation
4. action
5. module
module是考慮到使用單一狀態(tài)樹缭嫡,當(dāng)所有狀態(tài)集中到一個(gè)比較大的對(duì)象時(shí),如果應(yīng)用變得非常復(fù)雜抬闷, 那么store對(duì)象就可能變得相當(dāng)臃腫妇蛀,不便于開發(fā)與維護(hù)。
Vuex允許將store分割成多個(gè)模塊(module)笤成,每個(gè)模塊都擁有自己的state评架、mutation、action炕泳、getter纵诞,甚至能嵌套子模塊(module)。
注意:moduleX的聲明必須在store之前培遵。
const moduleA = {
state: {
count: 3
},
mutations: {
increment(state) {
state.count++
}
},
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
},
actions: {
incrementIfOddOnRootSum({state, commit, rootState}){
if((state.count + rootState.count) % 2 === 1){
commit('increment')
}
}
}
}
const moduleB = {
state: {
count: 8
},
mutations: {
login() {}
},
getters: {
login() {}
},
actions: {
login() {}
}
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
},
state: {
count: 2
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
},
getters: {
}
})
new Vue({
el: '#app',
store,
data: {
},
computed: {
}
});
對(duì)于模塊內(nèi)部的mutation和getter浙芙,接收的第一個(gè)參數(shù)是模塊的局部state,如moduleA的mutation與getter籽腕。
對(duì)于模塊內(nèi)部的action嗡呼,局部state通過'context.state暴露出來,根節(jié)點(diǎn)狀態(tài)則通過
context.rootState暴露出來节仿。如moduleA中的action晤锥。 對(duì)于模塊內(nèi)部的getter掉蔬,根節(jié)點(diǎn)state則作為第三個(gè)參數(shù)暴露出來廊宪。如moduleA中的getter。 **命名空間** 默認(rèn)情況下女轿,模塊內(nèi)部的action箭启、mutation、getter是注冊(cè)在全局命名空間的蛉迹,即假如你定義的moduleA與store都有increment()的mutation屬性傅寡,那當(dāng)你執(zhí)行:
store.commit('increment')`的時(shí)候,會(huì)將store與其子模塊里mutation的increment方法一起調(diào)用。如:
console.log(store.state.a.count); // 3
console.log(store.state.count); // 2
store.commit('increment');
console.log(store.state.a.count); // 4
console.log(store.state.count); // 3
如果你希望定義的模塊有更高的封裝度和復(fù)用性荐操,可以通過加入namespaced: true
的方式使其成為帶命名空間的模塊芜抒。當(dāng)模塊被注冊(cè)后,其所有的getter托启、action與mutation都會(huì)自動(dòng)根據(jù)模塊注冊(cè)的路徑調(diào)整命名宅倒。
例如給moduleA添加namespaced: true
,則執(zhí)行store.commit('increment')
時(shí)會(huì)自動(dòng)跳過moduleA屯耸。此時(shí):
const moduleA = {
namespaced: true
......
......
}
console.log(store.state.a.count); // 3
console.log(store.state.count); // 2
store.commit('increment');
console.log(store.state.a.count); // 3
console.log(store.state.count); // 3
此時(shí)若想執(zhí)行moduleA的increment拐迁,則要這樣寫:store.commit('a/increment')
,這樣便只執(zhí)行moduleA中的increment疗绣。
若moduleA嵌套moduleC线召,則在不聲明moduleC的namespaced情況下,其繼承父模塊moduleA的命名空間多矮。即:
const store = new Vuex.Store({
modules: {
account: {
namespaced: true,
// 模塊內(nèi)容(module assets)
state: { ... }, // 模塊內(nèi)的狀態(tài)已經(jīng)是嵌套的了缓淹,使用 `namespaced` 屬性不會(huì)對(duì)其產(chǎn)生影響
getters: {
isAdmin () { ... } // -> getters['account/isAdmin']
},
actions: {
login () { ... } // -> dispatch('account/login')
},
mutations: {
login () { ... } // -> commit('account/login')
},
// 嵌套模塊
modules: {
// 繼承父模塊的命名空間
myPage: {
state: { ... },
getters: {
profile () { ... } // -> getters['account/profile']
}
},
// 進(jìn)一步嵌套命名空間
posts: {
namespaced: true,
state: { ... },
getters: {
popular () { ... } // -> getters['account/posts/popular']
}
}
}
}
}
})
在帶命名空間的模塊內(nèi)訪問全局內(nèi)容
對(duì)于getter,會(huì)將rootState與rootGetters作為第三與第四參數(shù)傳入:
getters: {
someGetter (state, getters, rootState, rootGetters) {
rootState.count;
state.count;
getters.someOtherGetter;
rootGetters.someOtherGetter;
}
},
對(duì)于mutation與action塔逃,將(root: true}
作為第三參數(shù)傳給dispatch或commit即可:
actions: {
someAction({ dispatch, commit, getters, rootGetters }) {
getters.someGetter;
rootGetters.someGetter;
dispatch('someOtherAction'); // 不傳第三參數(shù)割卖,則從自身查找
dispatch('someOtherAction', null, { root: true }); // 傳入第三參數(shù),則從父模塊查找
commit('someMutation'); // 與上同理
commit('someMutation', null, { root: true });
}
}
那么對(duì)于mapState患雏、mapGetters鹏溯、mapMutations、mapActions這些函數(shù)來綁定帶命名空間的模塊時(shí)淹仑,直接寫可能有點(diǎn)繁瑣:
computed: {
...mapState({
a: state => state.some.nested.module.a,
b: state => state.some.nested.module.b
})
},
methods: {
...mapActions([
'some/nested/module/foo', // -> this['some/nested/module/foo']()
'some/nested/module/bar' // -> this['some/nested/module/bar']()
])
}
可以將模塊的空間名稱字符串作為第一個(gè)參數(shù)傳遞給上述函數(shù)丙挽,這樣所有綁定都會(huì)自動(dòng)將該模塊作為上下文。上面的例子可以簡(jiǎn)化為:
computed: {
...mapState('some/nested/module', {
a: state => state.a,
b: state => state.b
})
},
methods: {
...mapActions('some/nested/module', [
'foo', // -> this.foo()
'bar' // -> this.bar()
])
}
模塊動(dòng)態(tài)注冊(cè)
你可以在store創(chuàng)建之后匀借,動(dòng)態(tài)地用store.registerModule
方法給store創(chuàng)建模塊:
// 注冊(cè)模塊 `myModule`
store.registerModule('myModule', {
// ...
})
// 注冊(cè)嵌套模塊 `nested/myModule`
store.registerModule(['nested', 'myModule'], {
// ...
})
之后就能通過store,state.myModule
和store.state.nested.myModule
來訪問模塊的狀態(tài)颜阐。你也可以使用 store.unregisterModule(moduleName)
來動(dòng)態(tài)卸載模塊。但需要注意吓肋,你不可以卸載store聲明時(shí)的模塊凳怨,即靜態(tài)模塊。
保留state
在注冊(cè)新module時(shí)是鬼,如果你想保留過去的state肤舞,例如從服務(wù)器渲染的應(yīng)用保留state,可以通過preserveState
選項(xiàng)將其歸檔store.registerModule('a', module, { preserveState: true })
均蜜。
當(dāng)你設(shè)置 preserveState: true
時(shí)李剖,該模塊會(huì)被注冊(cè),action囤耳、mutation 和 getter 會(huì)被添加到 store 中篙顺,但是 state 不會(huì)偶芍。這里假設(shè) store 的 state 已經(jīng)包含了這個(gè) module 的 state 并且你不希望將其覆寫。
模塊重用
在實(shí)際開發(fā)中可能會(huì)有一個(gè)store中多次注冊(cè)同一個(gè)模塊德玫,或者多個(gè)store注冊(cè)同一個(gè)模塊的需求匪蟀,此時(shí)module被引用調(diào)用一次,其state就可能被重寫宰僧,導(dǎo)致store或模塊間數(shù)據(jù)互相污染萄窜。(類似于Vue組件內(nèi)的data會(huì)遇到同樣的問題)
解決辦法也類似,就是使用一個(gè)函數(shù)來聲明模塊狀態(tài):
const MyReusableModule = {
state () {
return {
foo: 'bar'
}
},
// mutation, action 和 getter 等等...
}