Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。
1. Vuex介紹
Vuex是官方的狀態(tài)管理工具枕扫,主要概念有State陪腌、Getter、Mutation铡原、Action偷厦、Module。
文章的開頭說過Vue的組件都是一個個Vue實例燕刻,Vuex也可以這樣來看,具體對比關(guān)系如下:
Vuex | 主要作用 | Vue組件 |
---|---|---|
State | 用來保存狀態(tài) | 相當(dāng)于data屬性 |
Getter | 用來對屬性進行組合修改 | 相當(dāng)于計算屬性 |
Mutation | 常用來直接改變State的值 | 相當(dāng)于methods |
Action | 主要用于提交Mutations,并且可以處理異步 | - |
Module | 用于模塊化狀態(tài)管理 | 模塊化Vue組件 |
使用Vuex只泼,我們用State保存狀態(tài),使用Getter來對狀態(tài)數(shù)據(jù)進行處理卵洗,使用Mutation來直接改變State的值请唱,用Action來提交Mutation,Action不是另類的Mutation,他操作的是Mutation中的函數(shù)过蹂,按照規(guī)范十绑,Action是不能直接修改State的,雖然它可以修改State酷勺,如果需要返回一個狀態(tài)或者一個異步的回調(diào)本橙,比如在Actions里面進行了http請求,可以直接返回一個Promise脆诉,使用Module,來對龐大的項目進行分組甚亭。
2. 對Vuex中State的獲取
一般情況下都會把Vuex全局注入到Vue中贷币,這時每一個Vue實例都能訪問到,一般都會使用計算屬性來接受Vuex的值亏狰,代碼如下:
//定義一個簡單的Vuex
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
Title: "123",
},
mutations: {
changeTitle(state, newTitle) {
state.Title= newTitle;
},
},
});
//組件中使用
<template>
<div class="test">{{NewState}}</div>
</template>
<script>
export default {
computed: {
NewState(){
return this.$store.state.Title
}
},
//其他必要代碼
}
</script>
這樣就能夠在頁面上顯示Vuex中的Title值了役纹。
除此之外還有一個輔助函數(shù),在本例子中代碼改為:
//組件中使用
<template>
<div class="test">{{Title}}</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState(['Title']),
},
//其他必要代碼
}
</script>
3. 對Vuex中State的改變
在上述Vuex例子中已經(jīng)聲明了Mutation,只需要把代碼改為:
//組件中使用
<template>
<input type="text" v-model="newTitle">
<div class="test">{{Title}}</div>
<button @click="changeTitle">改變Vuex中的title</button>
</template>
<script>
import { mapState } from 'vuex'
export default {
data(){
return{
newTitle:"",
} },
computed: {
...mapState(['Title']),
},
methods:{
chageTitle(){
this.$store.commit('changeTitle',this.newTitle)
},
},
//其他必要代碼
}
</script>
上述代碼主要是添加了一個輸入框和一個按鈕暇唾,輸入框輸入數(shù)據(jù)后促脉,點擊改變按鈕,就會觸發(fā)一個點擊事件策州,這個點擊事件中出發(fā)函數(shù)瘸味,函數(shù)調(diào)用Vuex中Mutation中的方法changeTitle,并傳過去了文本框中的值抽活,Mutation中的changeTitle則改變了State中的Title的值硫戈,因為組件中獲取Vuex的值使用計算屬性锰什,因此也會同步到頁面下硕。
當(dāng)然Mutation也有輔助函數(shù),只需在上面修改代碼如下:
//組件中使用
<template>
<input type="text" v-model="newTitle">
<div class="test">{{Title}}</div>
<button @click="changeTitles">改變Vuex中的title</button>
</template>
<script>
import { mapState,mapMutations } from 'vuex'
export default {
data(){
return{
newTitle:"",
} },
computed: {
...mapState(['Title']),
},
methods:{
...mapMutations(['changeTitle']),
changeTitles() {
this.changeTitle(this.newTitle)
},
// changeTitle() {
// this.$store.commit('changeTitle', this.newTitle)
// },
},
//其他必要代碼
}
</script>
4. Vuex中State的雙向綁定
Vuex的State的雙向綁定通常用在 form
表單汁胆,當(dāng)然也有其他需要雙向綁定的情況梭姓。
本小節(jié)主要應(yīng)用技術(shù)是Vue計算屬性的get
方法和set
方法,對上面例子修改后,代碼如下:
export default {
computed: {
newTitle: {
get() {
return this.$store.state.Title;
},
set(value) {
this.$store.commit('changeTitle', value)
}
}
},
}
把提交修改的事件添加到計算屬性的set方法中嫩码,就可以在值改變的時候觸發(fā)修改操作誉尖,從而改變State的值,這個操作在form表單中是可以行得通的铸题,因為表單一般用v-model綁定數(shù)據(jù)铡恕,而v-model是一個語法糖,它內(nèi)部擁有input事件丢间,可以觸發(fā)計算屬性的set方法探熔,而不用v-model的時候則不能觸發(fā)計算屬性的set方法,也就無法實現(xiàn)雙向綁定烘挫,這時候需要使用監(jiān)聽器監(jiān)聽newTitle值的變化诀艰,從而觸發(fā)改變State的Mutation方法,代碼如下:
export default {
computed: {
newTitle(){
return this.$store.state.Title;
}
},
watch:{
newTitle(newd,oldd){
this.$store.commit('changeTitle', newd)
}
}
}
因為監(jiān)聽器是在數(shù)據(jù)發(fā)生變化時執(zhí)行的饮六,所以能夠解決非v-model指令不能觸發(fā)set方法的問題其垄。
5. Vuex中對象屬性的深層監(jiān)聽
本小節(jié)是對上個小節(jié)的一點拓展。
在對Vuex的日常使用中卤橄,一般都不會一個屬性設(shè)置一個值绿满,一般都會進行分組,而模塊過大的時候就會啟用Vuex的Module窟扑,在一個設(shè)置配置的Vuex中喇颁,一般有如此配置:
//系統(tǒng)設(shè)置
config: {
events: true,
calls: false,
messages: false,
notifications: false,
sounds: false,
videoSounds: false
}
我們在組件中使用則會這樣用:
<script>
import { mapState } from 'vuex'
export default {
name: 'Drawer',
data() {
return {
docked: false,
position: 'left'
}
},
computed: {
...mapState(['config'])
}
}
</script>
在組件中就會使用v-model="config.events"
來使用寄月,這明顯是使用了v-model
的對象賦值,但是計算屬性并不會檢測內(nèi)部的變化无牵,從而觸發(fā)set方法漾肮,去提交對State修改的Mutation方法。
使用Vue的監(jiān)聽器設(shè)置deep:true
茎毁,監(jiān)聽器默認也不會監(jiān)聽內(nèi)部變化克懊,設(shè)置deep
為true
可以監(jiān)聽內(nèi)部變化,代碼如下:
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState(['config']),
},
watch: {
config: {
handler(newValue, oldValue) {
this.$store.commit('changeConfig', newValue)
},
deep: true
},
},
}
</script>
這樣就對config
的值進行了深層雙向綁定七蜘,而不是重復(fù)書寫這幾個配置屬性的get
和set
方法
6. Getter
前文提到過谭溉,Getter就像計算屬性。比如一個列表屬性橡卤,有時我們只需要其中一條數(shù)據(jù)我們可以這樣做:
export default new Vuex.Store({
state: {
list: [
{ id: 1, text: '我是1'},
{ id: 2, text: '我是2'}扮念,
{ id: 3, text: '我是3'}
]
},
getters: {
getone: state => {
return state.list.filter(id=> id===1)
}
}
})
這樣調(diào)用時,就只會返回第一條數(shù)據(jù)碧库。
在組件中使用:
computed: {
getOnes () {
return this.$store.getters.getone
}
}
值得注意的是柜与,雖然Vuex中Geeter類似于組件中的計算屬性,但是Getter并沒有像計算屬性那樣混入實例(按照計算屬性類比的話嵌灰,Vuex調(diào)用Getter需要使用this.$store.state.getone
)弄匕,但是Getter調(diào)用還是要this.$store.getters.getone
。
同樣可以使用助手函數(shù):
computed: {
...mapGetters([
'getone'
])
}
7. Action
Action是Vuex中的異步調(diào)用解決方法沽瞭,因為Mutation只能使用同步方法迁匠,Action操作的是Mutation中的函數(shù),下面有一個例子:
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
add(state) {
state.count+=1
}
},
actions: {
increment (context) {
context.commit('add')
}
}
})
在使用的時候可以這樣操作:
export default {
methods:{
addCount(){
this.$store.dispatch('add')
},
},
//其他必要代碼
}
同樣可以使用助手函數(shù):
import { mapActions } from 'vuex'
export default {
methods:{
...mapActions(['add']),
},
//其他必要代碼
}
如果需要在Action中傳遞值需要繞一下驹溃,代碼如下:
import { mapActions } from 'vuex'
export default {
data(){
return{
value:'',
};
},
methods: {
changeTitles() {
this.$store.dispatch('change',value)
},
//其他必要代碼
}
//vuex
export default new Vuex.Store({
state: {
Title: "test",
},
mutations: {
changevalue(state, newTitle) {
state.Title = newTitle;
}
},
actions: {
change(context, value) {
context.commit("changeTitle", value);
}
},
});
Action和Mutation類似城丧,都是第二個值才是傳入的值,因為Action是對Mutation的操作豌鹤,所以傳值需要多繞一下亡哄,但是還是可以達到預(yù)期效果的。
8. Module
當(dāng)你的系統(tǒng)非常龐大時傍药,把不同模塊的State和Mutation寫在一起會非常的亂磺平,因此Vuex還提供了模塊化。
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)
上面代碼是引用Vuex官方文檔的代碼,只需要在引用方法和操作時加上模塊名拐辽,就可以正確讀取到相應(yīng)模塊的值和方法拣挪。
操作方法 | 非模塊化 | 模塊化 |
---|---|---|
獲取State | this.$store.state.value; |
this.$store.state.[模塊名].value |
mapState | ...mapState(['value]) |
...mapState(['[模塊名]/value']) |
Mutation | this.$store.commit('event') |
this.$store.[模塊名].commit('event') |
mapMutation | ...mapMutation(['event]) |
...mapMutation(['[模塊名]/event']) |
Action和Getter類似。
使用webpack批量導(dǎo)入Modules
require.context
是webpack的一個用來管理依賴的一個函數(shù)俱诸,使用它可以批量導(dǎo)入菠劝,詳細查看文檔
https://webpack.docschina.org/guides/dependency-management/#require-context
具體使用:
import Vue from "vue";
import Vuex from "vuex";
import getters from "./getters";
Vue.use(Vuex);
// 引入modules下的所有文件
const modulesFiles = require.context("./modules", false, /\.js$/);
const modules = modulesFiles.keys().reduce((modules, path) => {
// ./app.js => app
const name = path.replace(/^\.\/(.*)\.\w+$/, "$1");
// 如果文件是空的則,下面這句取不出來結(jié)果
const value = modulesFiles(path);
modules[name] = value.default;
return modules;
}, {});
const store = new Vuex.Store({
modules,
getters
});
export default store;