1.vuex是什么
Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài)喷面,并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化偏灿。
上圖中綠色虛線包裹起來(lái)的就是vuex的核心丹诀,state保存的是公共狀態(tài),并只能通過(guò)mutation進(jìn)行更改菩混,而action可以異步的執(zhí)行mutation忿墅。如果你現(xiàn)在還不理解,沒(méi)有關(guān)系沮峡,看完下文的解釋和案例疚脐,相信你會(huì)有更好的理解。
2.為什么要用vuex
現(xiàn)如今的主流狀態(tài)管理器不管Vuex還是Redux都提倡單向數(shù)據(jù)流去管理狀態(tài)邢疙。簡(jiǎn)單的單向數(shù)據(jù)流是指用戶訪問(wèn)View棍弄,View發(fā)出用戶交互的Action,在Action里對(duì)state進(jìn)行相應(yīng)更新疟游。state更新后會(huì)觸發(fā)View更新頁(yè)面的過(guò)程呼畸。這樣數(shù)據(jù)總是清晰的單向進(jìn)行流動(dòng),便于維護(hù)并且可以預(yù)測(cè)颁虐。
一個(gè)簡(jiǎn)單的單向數(shù)據(jù)流模型:
但是蛮原,當(dāng)我們的應(yīng)用遇到多個(gè)組件共享狀態(tài)時(shí),單向數(shù)據(jù)流的簡(jiǎn)潔性很容易被破壞另绩。
- 多個(gè)視圖依賴于同一狀態(tài)
- 來(lái)自不同視圖的行為需要變更同一狀態(tài)
直接看這兩個(gè)問(wèn)題儒陨,你可能不是太懂花嘶,我們換一種方式來(lái)講:
試想:
如果一個(gè)父組件App.vue,下面有2個(gè)子組件:A.vue和B.vue蹦漠。App.vue想要和A.vue和B.vue通信可以使用props傳值的方式椭员。但如果A.vue和B.vue通信就很麻煩,必須通過(guò)App.vue自定義事件來(lái)實(shí)現(xiàn)笛园。
再試想:
如果App.vue下面有很多子組件隘击,這些子組件又依賴于其他組件。這樣的話研铆,父組件App.vue將要監(jiān)聽(tīng)大量的事件埋同,再分發(fā)給不同的子組件,組件之間的通信將會(huì)變得異常繁瑣蚜印。這不會(huì)是我們想要的組件化開(kāi)發(fā)體驗(yàn)莺禁。
好了,vuex就是為了解決這些問(wèn)題而出現(xiàn)的窄赋。
3.引入vuex
- 通過(guò)npm下載
npm install vuex --save
- 在main.js里添加
import Vuex from 'vuex'
Vue.use( Vuex );
const store = new Vuex.Store({
//待添加
})
new Vue({
el: '#app',
store,
render: h => h(App)
})
4.vuex的核心概念
vuex的核心概念有:State哟冬,Getters,Mutation忆绰,Action浩峡,Module。我們依次來(lái)看:
1.State
state就是Vuex中的公共的狀態(tài), 我們可以把state看作是所有組件的data错敢, 用于保存所有組件的公共數(shù)據(jù)翰灾。此時(shí),我們可以把所有組件公用的數(shù)據(jù)放到state里:
const store = new Vuex.Store({
state:{
users: [
{name: '鐘童', age: 18},
{name: '小明', age: 20},
{name: '李雷', age: 30},
{name: '小花', age: 22}
]
}
})
在我們的組件里可以通過(guò)這種方式來(lái)取到state里user的數(shù)據(jù):
export default {
data () {
return {
users : this.$store.state.users //獲取store中state的數(shù)據(jù)
}
}
}
2.Getters
Getters我們可以理解為computed屬性稚茅,可以認(rèn)為是store的計(jì)算屬性纸淮。getters的返回值會(huì)根據(jù)它的依賴被緩存起來(lái),且只有當(dāng)它的依賴值發(fā)生了改變才會(huì)被重新計(jì)算亚享。我們現(xiàn)在來(lái)添加一個(gè)getters:
- 這個(gè)getters的作用是用來(lái)計(jì)算我們的實(shí)際年齡(年齡減半)(不能笑)
// 以下核心概念均添加在store對(duì)象下
getters:{ //添加getters
realAge: (state) => {
let realAge = state.users.map( user => {
return {
name: user.name,
age: user.age / 2
}
})
return realAge;
}
}
同樣咽块,我們可以通過(guò)這種方式取到getters里realAge計(jì)算后返回的user:
export default {
data () {
return {
users : this.$store.getters.realAge
}
}
}
當(dāng)然,我們也可以使用官方提供的輔助函數(shù)...mapGetters,其實(shí)就是一個(gè)語(yǔ)法糖欺税。代碼如下:
import { mapGetters } from 'vuex'
computed:{
...mapGetters(['realAge'])
}
3.Mutation
mutation我們理解為methods屬性侈沪,mutation對(duì)象中保存著更改state狀態(tài)的回調(diào)函數(shù)。這個(gè)回調(diào)函數(shù)就是我們實(shí)際進(jìn)行狀態(tài)更改的地方晚凿。第一個(gè)參數(shù)是state 亭罪,第二個(gè)參數(shù)是payload,也就是自定義參數(shù)歼秽,這個(gè)參數(shù)通常在實(shí)際項(xiàng)目中以對(duì)象的形式傳入应役。
現(xiàn)在我們來(lái)添加一個(gè)mutation:
- 這個(gè)mutation的作用是用來(lái)修飾我們的名字
// 以下核心概念均添加在store對(duì)象下
mutations:{ //添加mutations
psName (state, payload ) {
let newName = state.users.forEach( user => {
user.name += payload
})
}
}
然后我們給一個(gè)button注冊(cè)點(diǎn)擊事件:
<template>
<div>
<button @click="psName">變有錢</button>
</div>
</template>
接著再通過(guò)點(diǎn)擊事件調(diào)用mutation:
psName() {
this.$store.commit('psName','明天彩票中獎(jiǎng)')
}
同樣,我們可以使用...mapMutation:
import { mapMutations } from 'vuex'
methods:{
...mapMutations(['psName'])
}
4.Action
action類似于mutation,不同在于:
- action提交的是mutation箩祥,而不是直接變更狀態(tài)
- action可以包含異步操作呻惕,而mutation只允許同步操作
- action通過(guò)dispatch來(lái)分發(fā)
action中第一個(gè)參數(shù)是一個(gè)上下文對(duì)象context,類似于canvas里的context滥比,可以取到store實(shí)例下的所有屬性和方法
現(xiàn)在我們來(lái)寫(xiě)一個(gè)action異步執(zhí)行剛才寫(xiě)的mutation:
// 以下核心概念均添加在store對(duì)象下
actions:{ //添加actions
psNameAsync( context, payload ) {
setTimeout( () => {
context.commit( 'psName', payload ); //context提交
}, 2000)
}
}
然后我們給一個(gè)button注冊(cè)點(diǎn)擊事件:
<template>
<div>
<button @click="psNameAsync">異步變有錢</button>
</div>
</template>
調(diào)用action:
psNameAsync() {
this.$store.dispatch('psNameAsync','明天彩票中獎(jiǎng)啦')
}
同樣,我們也可以使用...mapActions做院,方法同上盲泛,代碼不再贅述
5.module
由于使用單一狀態(tài)樹(shù),應(yīng)用的所有狀態(tài)會(huì)集中到一個(gè)比較大的對(duì)象键耕。當(dāng)應(yīng)用變得非常復(fù)雜時(shí)寺滚,store 對(duì)象就有可能變得相當(dāng)臃腫。
為了解決以上問(wèn)題屈雄,Vuex 允許我們將 store 分割成模塊(module)村视。每個(gè)模塊擁有自己的 state、mutation酒奶、action蚁孔、getter、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割:
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的狀態(tài)
store.state.b // -> moduleB 的狀態(tài)
有一點(diǎn)需要注意的是:
默認(rèn)情況下惋嚎,模塊內(nèi)部的 action杠氢、mutation 和 getter 是注冊(cè)在全局命名空間,而state則是注冊(cè)在模塊下的局部空間另伍。因此action鼻百、mutation 和 getter可以直接全局訪問(wèn),而state需要指定某個(gè)module摆尝。
但是:
如果希望你的模塊具有更高的封裝度和復(fù)用性温艇,你可以通過(guò)添加 namespaced: true 的方式使其成為帶命名空間的模塊。當(dāng)模塊被注冊(cè)后堕汞,它的所有 getter勺爱、action 及 mutation 都會(huì)自動(dòng)根據(jù)模塊注冊(cè)的路徑調(diào)整命名。例如:
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']
}
}
}
}
}
})
好了邻寿,看到這里你是不是對(duì)Vuex有了更好的理解了呢
【相關(guān)鏈接】