對(duì)于小型應(yīng)用來說饥漫,完全沒用必要引入狀態(tài)管理叽掘,因?yàn)檫@會(huì)帶來更多的開發(fā)成本。
對(duì)于組件化開發(fā)來講邻储,大型應(yīng)用的狀態(tài)往往跨越多個(gè)組件丙笋。在多層嵌套的父子組件之間傳遞狀態(tài)已經(jīng)十分麻煩答渔,而Vue更是沒用為兄弟組件提供直接共享數(shù)據(jù)的方法卦溢。
基于這個(gè)問題佳鳖,許多框架提供了解決方案——使用全局的狀態(tài)管理器,將所有分散的共享數(shù)據(jù)交由狀態(tài)管理器保管棚赔。
-
vuex
適用于大型項(xiàng)目中,在小型項(xiàng)目中使用sessionStorage
和localStorage
徘郭。
一靠益、什么是vuex?
1.1 vuex的定義
vuex
就是用來集中管理組件狀態(tài)残揉,稱為組件狀態(tài)管理模式胧后,適合組件中存在大量密集的數(shù)據(jù)傳遞的情況。
- 每一個(gè)Vuex應(yīng)用的核心都是一個(gè)
store
(倉庫)抱环,你也可以理解它是一個(gè)“非凡的全局對(duì)象”壳快。與普通的全局對(duì)象不同的是,基于Vue數(shù)據(jù)與視圖綁定的特點(diǎn)镇草,當(dāng)store中的狀態(tài)發(fā)生變化時(shí)眶痰,與之綁定的視圖也會(huì)被重新渲染。 - 渲染是一個(gè)單向的過程梯啤,因?yàn)閟tore中的狀態(tài)不允許被直接修改竖伯。
- 改變store中的狀態(tài)的唯一途徑就是顯式的提交(commit)
mutation
,這可以讓我們方便的跟蹤每一個(gè)狀態(tài)的變化因宇。
1.2 什么數(shù)據(jù)才需要存儲(chǔ)到Vuex中七婴?
- 一般情況下,只有組件之間共享的數(shù)據(jù)察滑,才有必要存儲(chǔ)到
vuex
(store)中打厘; - 對(duì)于組件中的私有數(shù)據(jù),依舊存儲(chǔ)在組件自身的data中即可贺辰。
1.3 使用Vuex統(tǒng)一管理的好處
- 能夠在vuex中集中管理共享的數(shù)據(jù)户盯,易于開發(fā)和后期的維護(hù)。
- 能夠高效的實(shí)現(xiàn)組件之間的數(shù)據(jù)共享魂爪,提高開發(fā)的效率先舷。
- 存儲(chǔ)在vuex中的數(shù)據(jù)都是響應(yīng)式的,能夠?qū)崟r(shí)保持?jǐn)?shù)據(jù)與頁面的同步滓侍。
二蒋川、Vuex核心概念
Vuex有5個(gè)重要的概念:State
,Getter
撩笆,Mutation
捺球,Action
缸浦,Module
。
1. State
State用于維護(hù)所有應(yīng)用層的狀態(tài)氮兵,并確保應(yīng)用只有唯一的數(shù)據(jù)源(SSOT裂逐,Single Source of Truth)。
- 用法:
//創(chuàng)建store數(shù)據(jù)源泣栈,提供唯一公共數(shù)據(jù)
const store = new Vuex.Store({
state: {
count: 0
}
})
組件訪問State中數(shù)據(jù)的方式有兩種:
this.$store.state.count
-
mapState
輔助函數(shù)映射下來
import { mapState } from 'vuex'
export default{
computed: {
...mapState(['count']) //...是ES6中的對(duì)象展開運(yùn)算符
}
}
2.Getter
Getter維護(hù)由State派生的一些狀態(tài)卜高,這些狀態(tài)隨著State狀態(tài)的變化而變化。
- 與計(jì)算屬性一樣南片,Getter中的派生狀態(tài)在被計(jì)算之后會(huì)被緩存起來掺涛。
- 當(dāng)重復(fù)調(diào)用時(shí),如果依賴的狀態(tài)沒有變化疼进,那么vuex不會(huì)重新計(jì)算派生狀態(tài)的值薪缆,而是直接采用緩存值。
- 用法:
const store = new Vuex.Store({
state: {
count: 0
},
getters: {
showNum (state) {//Vuex為其注入state對(duì)象
return '當(dāng)前最新的數(shù)量是【'+ state.count +'】'
}
}
})
Getter有兩種使用方式:
this.$store.getters.showNum
- 使用
mapGetters
輔助函數(shù)映射
import { mapGetters } from 'vuex'
export default {
computed:{
...mapGetters{[ 'showNum']}
}
}
3. Mutation
Mutation提供修改State狀態(tài)的方法伞广。
1.只能通過
mutation
變更Store
數(shù)據(jù)拣帽,不可以直接操作Store
中的數(shù)據(jù)
2.通過這種方式雖然操作起來稍微繁瑣一些,但是可以集中監(jiān)控所有數(shù)據(jù)的變化
- 用法:
new Vuex.Store ({
state : {
count: 0
},
Mutations : {
addCount (state, num){
state.count += num || 1
}
}
})
mutations有兩種觸發(fā)方式:
- 使用
store.commit
來提交mutation
methods:{
addCount () {
this.$store.commit('addCount')
}
}
- 使用
mapMutations
輔助函數(shù)映射
import { mapState,mapMutations } from 'vuex'
export default {
computed: {
...mapState(['count'])
},
methods: {
...mapMutations(['addCount']),
...mapMutations({ //為mutation賦別名嚼锄,注意沖突减拭,此方法不常用
increaseCount: 'addCount'
})
}
}
4. Action
Action用于處理異步任務(wù)。
Action類似于Mutation灾票,不同在
- Action不能直接修改狀態(tài)峡谊,只能通過提交mutation來修改
- Action可以包含異步操作
- 用法:
new Vuex.Store({
state: {
count: 0
},
mutations: {
addCount (state, num){
state.count += num || 1
}
},
actions: {
// context具有和store實(shí)例相同的屬性和方法
// 可以通過context獲取state和getters中的值,或者提交mutation和分發(fā)其他的action
addCountAsync(context, num){
setTimeout(()=>{
context.commit('addCountAsync', num || 1000)
}, num || 1000)
}
}
})
Action也有兩種觸發(fā)方式:
- 使用
store.dispatch
來分發(fā)action
methods: {
addCountAsync (num) {
this.$store.dispatch('addCountAsync ', num)
}
}
- 使用
mapActions
輔助函數(shù)映射
`import { mapState, mapActions } from 'vuex'`
export default{
computed: {
...mapState(['count'])
},
methods: {
...mapActions([ 'addCountAsync']),
...mapActions({ // 為action賦別名刊苍,注意沖突既们,此方法不常用
increaseCountAsync: 'addCountAsync'
})
}
}
5. Module
由于使用單一狀態(tài)樹,當(dāng)項(xiàng)目的狀態(tài)非常多時(shí)正什,store對(duì)象就會(huì)變得十分臃腫啥纸。
因此,Vuex允許我們將store分割成模塊(Module)婴氮,每個(gè)模塊擁有獨(dú)立的State斯棒、 Getter、 Mutation和Action主经,模塊之外還可以嵌套模塊荣暮,每一級(jí)都有著相同的結(jié)構(gòu)。
- 用法:
//定義模塊
const counter = {
namespaced: true, // 定義為獨(dú)立的命名空間
state: {
count: 0
},
getters: {
// 在模塊中罩驻,計(jì)算方法還會(huì)具有 rootState穗酥、rootGetters參數(shù)以獲取根模塊中的數(shù)據(jù)
tentimesCount (state, getters, rootState, rootGetters) {
console.log(state, getters, rootState, rootGetters)
return state.count * 10
}
},
mutaions: {
addCount (state, num) {
state.count += num || 1
}
},
actions: {
// context具有和store實(shí)例相同的屬性和方法
// 可以通過context獲取state和getters中的值,或者提交mutation和分發(fā)其他的action
// 在模塊中,context還會(huì)具有 rootState砾跃、rootGetters參數(shù)以獲取根模塊中的數(shù)據(jù)
addCountAsync (context, num) {
setTimeout(()=>{
context.commit('addCountAsync', num || 1000)
}, num || 1000)
}
}
}
新建倉庫
new Vuex.store({
modules: { // 注冊模塊
counter
}
})
在組件中骏啰,模塊的使用方法如下:
import {mapState, mapGetters, mapMutations , mapActions } from 'vuex'
export default {
computed: {
//輔助函數(shù)的第一個(gè)參數(shù)為模塊的名稱
...mapState('counter', ['count'])
...mapGetters('counter', ['tenTimesCount'])
},
methods: {
...mapMutations('counter', ['addCount'])
...mapActions('counter', ['addCountAsync'])
}
}
【總結(jié)】
作為一個(gè)狀態(tài)管理器,首先要有保管狀態(tài)的容器——State
抽高;
為了滿足衍生數(shù)據(jù)和數(shù)據(jù)鏈的需求判耕,從而有了Getter
;
為了可以“顯式地”修改狀態(tài)翘骂,所以需要Mutation
壁熄;
為了可以“異步地”修改狀態(tài)(滿足AJAX等異步數(shù)據(jù)交互),所以需要Action
碳竟;
最后请毛,如果應(yīng)用有成百上千個(gè)狀態(tài),放在一起會(huì)顯得十分龐雜瞭亮,所以需要分模塊管理(Module
)。
三固棚、在項(xiàng)目中使用Vuex
- 安裝插件
npm install vuex --save-dev
或者cnpm install vuex --save-dev
使用npm或者cnpm都可以 - 創(chuàng)建文件
在src目錄下創(chuàng)建store
统翩、store/index.js
、store/modules
此洲、store/modules/counter.js
其中厂汗,
store
是我們進(jìn)行Vuex倉庫開發(fā)的工作目錄,
store/index.js
是倉庫的輸出文件呜师,
store/modules
目錄用于放置各個(gè)模塊娶桦,
store/modules/counter.js
文件是一個(gè)加數(shù)器模塊。- store/modules/counter.js中的代碼如下:
export default { namespaced: true, // 定義為獨(dú)立的命名空間 state: { count: 0 }, getters: { // 在模塊中汁汗,計(jì)算方法還會(huì)具有 rootState衷畦、rootGetters參數(shù)以獲取根模塊中的數(shù)據(jù) tentimesCount (state, getters, rootState, rootGetters) { console.log(state, getters, rootState, rootGetters) return state.count * 10 } }, mutaions: { addCount (state, num) { state.count += num || 1 } }, actions: { // context具有和store實(shí)例相同的屬性和方法 // 可以通過context獲取state和getters中的值,或者提交mutation和分發(fā)其他的action // 在模塊中知牌,context還會(huì)具有 rootState祈争、rootGetters參數(shù)以獲取根模塊中的數(shù)據(jù) addCountAsync (context, num) { setTimeout(()=>{ context.commit('addCountAsync', num || 1000) }, num || 1000) } } }
- store/index.js中的代碼如下:
上面已經(jīng)實(shí)例化了一個(gè)Vuex倉庫并構(gòu)建了一個(gè)加數(shù)器模塊。之后角寸,我們要在Vue實(shí)例中引入這個(gè)倉庫菩混,這還需要修改兩個(gè)文件: webpack的入口文件main.js和單組件文件 components/HelloWorld.vue.import Vue from 'vue' import Vuex from 'vuex' import counter from './modules/counter' //引入加數(shù)器模塊 Vue.use(Vuex) //安裝插件 export default new Vuex.Store {( //實(shí)例化Vuex倉庫 modules: { counter } )}
- 修改后的main.js的代碼如下:
到此就已經(jīng)將倉庫注冊到了Vue實(shí)例中了,之后就可以在項(xiàng)目的任意文件中使用了扁藕。import Vue from 'vue' import App from './App' import router from './router' import store from './store' Vue.config.productionTip = false new Vue({ el: #app, router, store, components: {App}, template: '<App/>' })