一篇文章帶你吃透Vuex3的狀態(tài)管理
一. Vuex是什么
了解過Vuex的都知道,它是Vue官方的狀態(tài)管理方案;可以對(duì)父子,祖孫以及兄弟組件之間進(jìn)行通信;
除了使用Vuex,我們都知道還有一個(gè)方案能滿足任何組件的之間的通信,那就是vue全局事件總線($ bus);
在數(shù)據(jù)接收組件內(nèi)利用$ on綁定一個(gè)事件(eventName),第二個(gè)參數(shù)綁定一個(gè)回調(diào)函數(shù)來接受數(shù)據(jù)。
//接收數(shù)據(jù)的組件
this.$bus.$on('eventName',(value)=>{
console.log(value) //{a:1,b:2}
})
在數(shù)據(jù)發(fā)送組件內(nèi)利用$ emit 提交給綁定的事件(eventName),后面的參數(shù)為要傳遞的數(shù)據(jù);
//發(fā)送數(shù)據(jù)的組件
var obj = {a:1,b:2}
this.$emit('eventName',obj)
那既然Vue全局事件總線($ bus)能夠滿足任何組件的之間的通信,為什么vue官方還要再創(chuàng)造出Vuex呢?
Vue全局事件總線
我們來看下面這個(gè)場(chǎng)景(利用事件總線進(jìn)行數(shù)據(jù)共享)圖片來源于尚硅谷

上圖可以看到,A組件中data中有一個(gè)X屬性,B,C,D組件也都需要,共享屬性可以利用$ on 去獲取到X屬性;這樣看起來,感覺不是很簡(jiǎn)單嗎,沒有什么啊,別急,這只是讀取屬性,那如果B,C,D需要修改呢?
其實(shí)只需要在B,C,D組件內(nèi)去利用$ emit事件把修改的值發(fā)送給A組件,A組件再通過$ on去接受然后對(duì)X屬性進(jìn)行修改,光看文字是不是感覺已經(jīng)很繞啦,也就是下圖所示:圖片來源于尚硅谷

紅色箭頭是B,C,D組件讀取到共享屬性X,綠色箭頭是B,C,D組件修改X屬性;
目前場(chǎng)景只是展示四個(gè)組件都需要一個(gè)共享屬性X,通過讀寫,看上去都已經(jīng)很亂啦,那如果大項(xiàng)目中有十幾個(gè),幾十個(gè)組件都需要一個(gè)共享屬性X呢,豈不是更亂;
Vuex狀態(tài)管理
那如果要用Vuex實(shí)現(xiàn)X屬性的共享呢?看下圖:圖片來源于尚硅谷

Vuex是獨(dú)立于任何組件的一個(gè)存在,把A,B,C,D組件需要共享的屬性放到Vuex中去管理,不需要共享的屬性還是寫在原組件內(nèi);此時(shí)A,B,C,D組件和Vuex是雙向箭頭,就是組件既可以通過其內(nèi)置的api去讀,也可以去修改,某一個(gè)組件修改共享的屬性,其他組件獲取的都是最新修改的數(shù)據(jù);
何時(shí)使用Vuex
Vue事件總線其實(shí)也很方便,但是適合使用小項(xiàng)目中;對(duì)于大項(xiàng)目Vuex作為共享狀態(tài)集中式管理,是最好的選擇,方便使用以及維護(hù);
那疑問來了,我也不清楚項(xiàng)目的大小怎么辦,什么時(shí)候適合使用Vuex呢?
- 項(xiàng)目中多個(gè)組件都需要使用或修改共同一個(gè)狀態(tài)(多個(gè)組件共享同一個(gè)狀態(tài))
二. 純vue組件案例
本來打算直接介紹引入Vuex的代碼步驟和方法,但是為了更好的理解好對(duì)比,我先把我寫的兩個(gè)組件案例demo和代碼給大家看一下,稍后再給大家看引入Vuex后的代碼,雖然功能都一模一樣,主要是對(duì)比Vuex使用前后的組件內(nèi)部代碼不同;
計(jì)算總數(shù)案例
導(dǎo)航二的計(jì)算總數(shù)案例組件:

代碼如下:
<template>
<div>
<h3 style="marginBotton:10px;">此時(shí)導(dǎo)航三組件內(nèi)總?cè)藬?shù)為:???</h3>
<h3 :style="{marginBottom:'10px'}">當(dāng)前計(jì)算的和為:{{count}}</h3>
<el-select v-model.number="value" size="mini" :style="{width:'60px'}" placeholder="0">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<el-button size="mini" @click="jia">+</el-button>
<el-button size="mini" @click="jian">-</el-button>
<el-button size="mini" @click="oddJia">和為奇數(shù) +</el-button>
<el-button size="mini" @click="asyncJia">等1秒后 +</el-button>
</div>
</template>
<script>
export default {
data(){
return{
count:0,
options: [
{value: 1,label: '1'},
...
],
value: null
}
},
methods:{
jia(){
this.count += this.value
},
jian(){
this.count -= this.value
if(this.count <= 0){
this.count = 0
}
},
oddJia(){
if(this.count%2 !== 0){
this.jia()
}
},
asyncJia(){
setTimeout(() => {
this.jia()
},1000)
}
}
}
</script>
添加人員案例
導(dǎo)航三添加人員案例組件:

代碼如下:
<template>
<div>
<el-select v-model="value" placeholder="請(qǐng)選擇添加人員">
<el-option
v-for="item in options"
:key="item.name"
:label="item.name"
:value="item.name">
</el-option>
</el-select>
<el-button @click="addPerson">添加</el-button>
<el-button @click="rest">還原</el-button>
<el-table
:data="filterTable"
:border='true'
style="width: 100%;marginTop:10px">
<el-table-column
align="center"
prop="name"
label="姓名">
</el-table-column>
...
</el-table>
<h3 style="marginTop:10px;">此時(shí)表格總?cè)藬?shù)為:{{filterTable.length}}</h3>
<h3 style="marginTop:10px;">此時(shí)導(dǎo)航二組件的計(jì)算總和為:???</h3>
</div>
</template>
<script>
export default {
data() {
return {
value:'',
options: [
{name: '王朝', sex:'男',age:21,hobby:'武術(shù)'},
...
],
tableData: [
{name: '張三', sex:'男',age:18,hobby:'唱歌'},
{name: '李四', sex:'女',age:20,hobby:'跳舞'},
],
filterTable:[]
}
},
mounted(){
this.filterTable.push(...this.tableData)
},
methods:{
addPerson(){
var isHave = true
this.filterTable.forEach( e => {
if(e.name == this.value) isHave = false
})
if(!isHave){
this.$message({
message: '人員已添加!',
type: 'warning',
duration:1000
})
return
}
var person = this.options.filter( item => {
return item.name == this.value
})
this.filterTable.push(person[0])
},
rest(){
this.filterTable = this.tableData
}
}
}
</script>
此時(shí)兩個(gè)組件是完全獨(dú)立的,沒有實(shí)現(xiàn)數(shù)據(jù)共享,所以在用到對(duì)方組件內(nèi)數(shù)據(jù)的地方以 ”???“ 標(biāo)記;
接下來,我們就開始一步一步使用Vuex去實(shí)現(xiàn)兩個(gè)組件的數(shù)據(jù)共享;
三. Vuex工作原理和流程
下面是官方給的Vuex的工作原理圖,如下:

如果想要很熟悉的使用Vuex,那我們就應(yīng)該先了解其工作原理,明白了它內(nèi)部的運(yùn)轉(zhuǎn)機(jī)制,那么我們就可以告別死記,就可以很熟悉流暢的編寫代碼;
從上圖工作原理圖來看Vuex中有三個(gè)最重要的核心對(duì)象,Actions,Mutations,State,那他們?nèi)齻€(gè)是什么關(guān)系,怎么協(xié)助運(yùn)轉(zhuǎn)呢,下面我們來看一下Vuex的工作流程;
第一種工作流程
為了方便了解,我給Vuex工作原理圖稍微做了一些標(biāo)注,圖如下:

這個(gè)紅色箭頭就是組件使用Vuex的工作流程,配合上圖,認(rèn)真理解下面文字,看一下組件是怎么修改共享數(shù)據(jù)的:
- vue組件內(nèi)調(diào)用dispacth()函數(shù),該函數(shù)內(nèi)又兩個(gè)參數(shù),第一個(gè)參數(shù)是一個(gè)方法名‘key1’,第二個(gè)參數(shù)是傳入的值
- 由于的組件調(diào)用了dispatch,就會(huì)在Actions對(duì)象中去找dispatch函數(shù)傳入的一個(gè)參數(shù)‘key1’;
- 找到key1后,就會(huì)調(diào)用對(duì)應(yīng)的函數(shù);key1函數(shù)內(nèi)部又調(diào)用了commit()函數(shù)。commit()函數(shù)也有兩個(gè)參數(shù),第一個(gè)參數(shù)是一個(gè)方法名‘KEY1’,第二個(gè)參數(shù)是傳入的值;
- 由于調(diào)用了commit,就會(huì)在Mutations對(duì)象中去找commit函數(shù)傳入的第一個(gè)參數(shù)‘KEY1’;
- 在Mutations對(duì)象找到‘KEY1’對(duì)應(yīng)的函數(shù)后,Vuex內(nèi)部調(diào)用Mutate對(duì)狀態(tài)就行修改,修改狀態(tài)后對(duì)修改狀態(tài)的組件進(jìn)行render
第二種工作流程
現(xiàn)在應(yīng)該了解其工作的大致流程了吧,別急,還沒有完,我們繼續(xù)看下圖:

這個(gè)應(yīng)該是最完善的工作流程圖,除了剛才我們介紹的最外圈的工作流程外,其實(shí)組件也可以直接去調(diào)用commit()去修改狀態(tài);那該怎么區(qū)分和使用呢?
- Vuex內(nèi)部有規(guī)定,Actions中主要寫一些復(fù)雜的業(yè)務(wù)邏輯和異步處理,Mutations中主要修改狀態(tài);
- 如果在組件內(nèi)就可以拿到需要傳遞的value值,內(nèi)部不在需要對(duì)value進(jìn)行過多的業(yè)務(wù)了邏輯處理,可以直接commit()去Mutations中調(diào)用修改狀態(tài)函數(shù)
- 如果需要傳遞的value值要通過接口獲取,然后還要進(jìn)行復(fù)雜的業(yè)務(wù)邏輯,最好放到Actions的函數(shù)去處理;
- 這樣做的好處是Devtools開發(fā)工具可以準(zhǔn)確的監(jiān)控Vuex狀態(tài)的變化;
生活化的Vuex工作原理
以上就是Vuex的全部工作原理以及流程;但是為了讓大家更好的去記住和理解,我又模擬了下面這個(gè)場(chǎng)景:

Vuex就相當(dāng)于一個(gè)飯店,vue組件就是顧客,Actions就是服務(wù)員,Mutations就是廚師,state就是做出來的菜和飯;我們把修改共享狀態(tài)的任務(wù)改成點(diǎn)菜,我們可以走一下點(diǎn)餐流程:
通過服務(wù)員點(diǎn)餐的流程:
- 首先,顧客因?yàn)椴皇煜わ埖瓴似伏c(diǎn)菜的時(shí)候需要詢問,所以要找‘服務(wù)員1’點(diǎn)菜,或者是通過掃描桌上外賣平臺(tái)點(diǎn)餐
- 其次,‘服務(wù)員1’整理好菜單后就交給了后廚‘廚師1’去做。后廚安裝的有監(jiān)控,以后想要知道哪個(gè)廚師做的哪個(gè)顧客的菜都能查詢到;
- 最后,廚師1做好菜,顧客就可以直接拿到;
顧客自己點(diǎn)餐的流程:
- 首先,顧客很熟悉這家飯店,菜系了解,廚師也都認(rèn)識(shí)這樣就很簡(jiǎn)單,直接找到‘廚師1’要了幾個(gè)菜;
- 最后,廚師1做好菜,顧客就可以直接拿到;
現(xiàn)在應(yīng)該很熟悉Vuex的工作流程了吧,很簡(jiǎn)單,學(xué)會(huì)點(diǎn)菜就行,哈哈,那接下來我們就要開始使用Vuex啦;
四. 在項(xiàng)目中引入Vuex
安裝Vuex
首先,你的電腦要安裝node環(huán)境,使用npm安裝:
npm install vuex --save
創(chuàng)建store
然后在項(xiàng)目src文件夾下創(chuàng)建一個(gè)store文件夾,在其里面創(chuàng)建index.js:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {},
getters:{},
mutations: {},
actions:{},
modules:{}
})
export default store
在Vue中掛載store
最后在main.js文件內(nèi)引入創(chuàng)建的store:
import Vue from 'vue'
import App from './App'
import store from './store'
new Vue({
el: '#app',
components: { App },
template: '<App/>',
store
})
五. Vuex的核心屬性用法
知道一個(gè)組件怎么使用,另一個(gè)就不在話下,這里我以計(jì)算總數(shù)組件為例;
在創(chuàng)建store實(shí)例的時(shí)候,大家可以看到Vuex內(nèi)部有五個(gè)核心屬性,下面我們就從這五個(gè)核心屬性入手,一步一步實(shí)現(xiàn)計(jì)算總數(shù)案例的所有原有功能;
刪除掉計(jì)算總數(shù)組件所有邏輯代碼,此時(shí)demo,如下圖:

代碼如下:
<template>
<div>
<h3 style="marginBotton:10px;">此時(shí)導(dǎo)航三組件內(nèi)總?cè)藬?shù)為:???</h3>
<h3 :style="{marginBottom:'10px'}">當(dāng)前計(jì)算的和為:???</h3>
<el-select v-model.number="value" size="mini" :style="{width:'60px'}" placeholder="0">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<el-button size="mini" @click="jia">+</el-button>
<el-button size="mini" @click="jian">-</el-button>
<el-button size="mini" @click="oddJia">和為奇數(shù) +</el-button>
<el-button size="mini" @click="asyncJia">等1秒后 +</el-button>
</div>
</template>
<script>
export default {
data(){
return{
options: [
{value: 1,label: '1'},
...
],
value: null
}
},
methods:{
jia(){},
jian(){},
oddJia(){},
asyncJia(){ }
}
}
</script>
單一數(shù)據(jù)源(state)
首先是state配置,它的值是一個(gè)對(duì)象,用來存儲(chǔ)共享狀態(tài);Vuex使用單一樹原則,將所有的狀態(tài)都放到這個(gè)對(duì)象上,便于定位和維護(hù);
我們把計(jì)算總數(shù)組件的count放到Vuex中的state中,如下:
...
const store = new Vuex.Store({
state: {
count:0
},
})
...
組件中獲取,可以這樣:
...
computed:{
count(){
return this.$store.state.count
}
},
...
如果組件內(nèi)需要引入的共享狀態(tài)屬性較多,都使用this.$store.state.x的話,會(huì)比較麻煩,所Vuex給我們提供了mapState()輔助函數(shù)來獲取,如下:
import {mapState} from 'vuex'
...
computed:{
//參數(shù)為對(duì)象寫法
...mapState({
//key值隨意自定義,模板中插值也要寫入自定義的key值
count:'count'
})
//參數(shù)為數(shù)組寫法
// 此時(shí)組件定義的屬性名要和state中定義的屬性名一致
...mapState(['count'])
},
...
以上兩種獲取方式,組件都可以獲取到共享的count屬性,并顯示到頁面上,如下圖:

狀態(tài)更新方式(mutations)
Vuex中的狀態(tài)和組件中的狀態(tài)不同,不能直接 state.count = ‘xx’ 這種方式去修改;Vuex修改狀態(tài)的唯一方式就是提交mutation;
mutation是一個(gè)函數(shù),第一個(gè)參數(shù)為state,它的作用就是更改state的狀態(tài);
下面我們就來定義一個(gè)mutation,在函數(shù)內(nèi)去更新count這個(gè)狀態(tài):
const store = new Vuex.Store({
state: {
count:0
},
mutations: {
//更改count方法,type為增加
JIA(state,count){
state.count += count
}
}
})
組件中提交commit函數(shù)去觸發(fā)mutattions中定義的JIA函數(shù):
...
methods:{
jia(){
this.$store.commit('JIA',this.value)
}
}
...
或者是利用最常用的mapMutations輔助函數(shù),如下:
...
//把獲取的單選框的value值,通過參數(shù)傳給mutation定義的jia函數(shù),
<el-button size="mini" @click="JIA(value)">+</el-button>
...
import {mapMutations} from 'vuex'
...
methods:{
//對(duì)象參數(shù)中的key值為自定義的點(diǎn)擊事件名
...mapMutations({JIA:'JIA'}),
//參數(shù)為數(shù)組,此時(shí)點(diǎn)擊事件名要和mutation中定義的增加事件名一致
...mapMutations(['JIA',]),
}
以上兩種方法都能實(shí)現(xiàn)增加功能,如下圖:

store中的計(jì)算屬性(getters)
有時(shí)候我們需要從 store 中的 state 中派生出一些狀態(tài),例如對(duì)列表進(jìn)行過濾并計(jì)數(shù),這里我們就簡(jiǎn)單的把計(jì)算總數(shù)和隨之增加10倍:
Getter 接受 state 作為其第一個(gè)參數(shù),如下:
const store = new Vuex.Store({
state: {
count:0
},
getters:{
multipleCount(state){
return state.count * 10
}
}
})
組件中可以這樣去獲取,如下:
<h3>當(dāng)前計(jì)算的和的10倍為:{{multipleCount }}</h3>
...
computed:{
multipleCount(){
return this.$store.getters.multipleCount
},
},
或者利用輔助函數(shù)mapGetters獲取,和mapState引入一樣,如下:
import {mapGetters} from 'vuex'
...
computed:{
//對(duì)象寫法
...mapGetters({
multipleCount: 'multipleCount'
}),
//數(shù)組寫法
//此時(shí)自定義的函數(shù)名要和getter中定義的函數(shù)名一致
...mapGetters(['multipleCount']),
},
以上兩種方式都可以獲取到getter函數(shù),如下圖:

異步更新狀態(tài)(actions)
Action 類似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接變更狀態(tài)。
- Action 可以包含任意異步操作。
最大的區(qū)別就是action中可以寫一些業(yè)務(wù)邏輯和異步操作;而mutation就是簡(jiǎn)單的變更狀態(tài);
怎么說呢,其實(shí)action很像一個(gè)連接組件和mutation的中介,對(duì)于同步和異步操作在action中都能完成,它只是幫我們?cè)赼ctions函數(shù)中commit提交了mutations中的函數(shù);
同步增加總數(shù)
下面我們來再一次用action來實(shí)現(xiàn)點(diǎn)擊JIA函數(shù)增加總數(shù)(同步操作),如下:
const store = new Vuex.Store({<!--{C}%3C!%2D%2D%20%2D%2D%3E--> state: {<!--{C}%3C!%2D%2D%20%2D%2D%3E--> count:0 }, mutations: {<!--{C}%3C!%2D%2D%20%2D%2D%3E--> JIA(state,count){<!--{C}%3C!%2D%2D%20%2D%2D%3E--> state.count += count } }, actions:{<!--{C}%3C!%2D%2D%20%2D%2D%3E--> //第一個(gè)參數(shù)為context(上下文),第二個(gè)參數(shù)為傳的值 jia(context,value){<!--{C}%3C!%2D%2D%20%2D%2D%3E--> console.log(context) //打印結(jié)果如下圖 context.commit('JIA',value) } },})這就是 console.log(context) 的結(jié)果,是一個(gè)對(duì)象,里面包含了commit,dispatch,getters等方法,像一個(gè)小型的srore,但是跟store不一樣,我們這里就叫上下文;

既然context是個(gè)對(duì)象,如果我們只需要commit的話,也可以使用數(shù)據(jù)解構(gòu)的方式寫,入下圖:
actions:{
//第一個(gè)參數(shù)為context(上下文),第二個(gè)參數(shù)為傳的值
jia({commit},value){
commit('JIA',value)
}
},
組件可以分發(fā)action,通過 store.dispatch 方法觸發(fā):
methods:{
jia(){
this.$store.dispatch('jia', this.value)
},
}
或者通過mapActions輔助函數(shù)來觸發(fā):
import { mapActions} from 'vuex'
...
methods:{
//對(duì)象參數(shù)中的key值為自定義的點(diǎn)擊事件名
...mapActions({jia:'jia'}),
//數(shù)組參數(shù)
//此時(shí)點(diǎn)擊事件名要和action中定義的增加事件名一致
...mapActions(['jia']),
}
我們利用actions和mutations都可以實(shí)現(xiàn)JIA函數(shù)的功能使總數(shù)增加;那問題來了,我們可以調(diào)用mutations里面定義的JIA函數(shù),為什么還要多此一舉的在actions中去調(diào)用mutation,然后再去分發(fā)actions呢;
當(dāng)然這里我們只是演示給大家作為參考,一般同步操作我們也不會(huì)多此一舉的放入actions里面,項(xiàng)目中直接更新狀態(tài)我們就直接在組件commit提交mutation函數(shù)就可以;
異步增加總數(shù)
由于項(xiàng)目沒有寫后臺(tái)服務(wù),是mock的數(shù)據(jù),這里我們異步就用定時(shí)器來寫,如下圖:
actions:{
asyncJia({commit},value){
setTimeout(() => {
context.commit('JIA',value)
},1000)
}
},
組件中分發(fā)action方法如下:
methods:{
asyncJia(){
this.$store.dispatch('asyncJia',this.value)
}
}
一樣,也可以利用mapActions函數(shù),如下:
import { mapActions} from 'vuex'
...
methods:{
// //對(duì)象參數(shù)中的key值為自定義的點(diǎn)擊事件名
...mapActions({asyncJia:'asyncJia'}),
//數(shù)組參數(shù),此時(shí)點(diǎn)擊事件名要和action中定義的增加事件名一致
...mapActions(['asyncJia']),
}
不管是同步和異步操作,以上方法都能實(shí)現(xiàn)增加總數(shù)的功能;
為什么actions中處理異步
那問題來了,為什么actions中可以處理異步操作,而不能在mutations中進(jìn)行呢?
以上屬于簡(jiǎn)單的一些業(yè)務(wù)邏輯,action中的定時(shí)器或者奇數(shù)判斷其實(shí)在mutation中就可以完成,這樣感覺action就像是多余的一樣;
官方明確表示:
- mutations里同步的意義在于,每一個(gè)mutation執(zhí)行完畢之后,可得到對(duì)應(yīng)的狀態(tài),方便Vuex的devtools工具可以跟蹤狀態(tài)的變化
- 如果在mutations中寫入異步,devtools工具就沒法知道狀態(tài)是什么時(shí)候更新的,所以才有了actions
- actions用來專門處理異步,里面觸發(fā)mutations,就可以很清楚的看到mutation是何時(shí)被記錄下來的,并且可以立即查看對(duì)應(yīng)的狀態(tài),這樣異步更新也可以看到更新狀態(tài)的流程;
舉個(gè)例子,還有就是遇到一些復(fù)雜的業(yè)務(wù)邏輯,第二個(gè)異步操作可能會(huì)依賴第一個(gè)操作的結(jié)果;
代碼如下:
actions:{
asyncJia({commit},value){
return new Promise((resolve,reject) => {
setTimeout(() => {
context.commit('JIA',value)
resolve()
},1000)
})
}
},
組件內(nèi)調(diào)用此異步增加函數(shù)后,返回結(jié)果后再等1秒調(diào)用增加函數(shù),代碼如下:
...
<el-button size="mini" @click="doubleAsync(value)">異步 + 后再等1秒后 +</el-button>
...
import {mapMutations, mapActions} from 'vuex'
...
methods:{
...mapMutations(['JIA','JIAN']),
...mapActions(['oddJia','asyncJia']),
doubleAsync(){
this.asyncJia(this.value).then(() => {
setTimeout(() => {
this.JIA(this.value)
},1000)
})
}
}
demo案例如下圖:

分工明確,這樣看著清晰明了,操作起來也很方便;
擴(kuò)展:其實(shí)在matations中也可以實(shí)現(xiàn)異步操作,或者不需要mutations,直接在action中去更改state狀態(tài),頁面功能也都可以正常使用并顯示;但是上面說到了,如果Vuex沒有操作規(guī)則,大家都隨心所欲的去編寫邏輯代碼,devtools工具也不會(huì)正確跟蹤,那樣也不便于后期維護(hù),代碼一樣很亂,所以我們還是聽從Vuex官方推薦的標(biāo)準(zhǔn)去使用;
還有一個(gè)modules模塊沒有介紹,不要著急,目前我們只是實(shí)現(xiàn)了單個(gè)組件與Vuex關(guān)聯(lián),只是介紹了核心屬性的api用法,還沒有實(shí)現(xiàn)多組件數(shù)據(jù)共享;
下面就把項(xiàng)目中兩個(gè)組件的詳細(xì)的Vuex版本代碼分享出來,作為參考。
六. 使用Vuex組件的完整代碼
store倉(cāng)庫(kù)代碼
import Vue from 'vue'
import Vuex from 'vuex'
import {message} from 'element-ui'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count:0,
filterTable:[]
},
getters:{
multipleCount(state){
return state.count * 10
}
},
mutations: {
JIA(state,count){
state.count += count
},
JIAN(state,count){
state.count -= count
if(state.count <= 0) state.count = 0
},
ADDPERSON(state,person){
state.filterTable.push(...person)
},
//這個(gè)重置方法本不需要,只是為了組件使用commit
//dispatch('restPerson')就可以實(shí)現(xiàn),這里為了下面講解知識(shí)點(diǎn)而用
RESTPERSON(state,person){
state.filterTable = []
state.filterTable.push(...person)
}
},
actions:{
asyncJia(context,value){
return new Promise((resolve,reject) => {
setTimeout(() => {
context.commit('JIA',value)
resolve()
},1000)
})
},
oddJia({commit,state},value){
if(state.count%2 !== 0){
commit('JIA',value)
}
},
addPerson({commit,state},obj){
var isHave = true
state.filterTable.forEach( e => {
if(e.name == obj.value) isHave = false
})
if(!isHave){
message({
message: '人員已添加!',
type: 'warning',
duration:1000
})
return
}
var person = obj.options.filter( item => {
return item.name == obj.value
})
commit('ADDPERSON',person)
},
restPerson({commit,state},value){
state.filterTable = []
commit('ADDPERSON',value)
}
},
})
export default store
計(jì)算總數(shù)案例
導(dǎo)航二計(jì)算總數(shù)案例組件代碼,如下:
<template>
<div>
<h3 style="marginBotton:10px;">此時(shí)導(dǎo)航三組件內(nèi)總?cè)藬?shù)為:???</h3>
<h3>當(dāng)前計(jì)算的和為:{{count}}</h3>
<h3 :style="{marginBottom:'10px'}">當(dāng)前計(jì)算的和的10倍為:{{multipleCount }}</h3>
<el-select v-model.number="value" size="mini" :style="{width:'60px'}" placeholder="0">
...
</el-select>
<el-button size="mini" @click="JIA(value)">+</el-button>
<el-button size="mini" @click="JIAN(value)">-</el-button>
<el-button size="mini" @click="oddJia(value)">和為奇數(shù) +</el-button>
<el-button size="mini" @click="asyncJia(value)">等1秒后 +</el-button>
<el-button size="mini" @click="doubleAsync(value)">異步 + 后再等1秒后 +</el-button>
</div>
</template>
<script>
import {mapState,mapGetters,mapMutations, mapActions} from 'vuex'
export default {
data(){
return{
options: [
{value: 1,label: '1'},
...
],
value: null
}
},
computed:{
...mapGetters(['multipleCount']),
...mapState(['count'])
},
methods:{
...mapMutations(['JIA','JIAN']),
...mapActions(['oddJia','asyncJia']),
doubleAsync(){
this.asyncJia(this.value).then(() => {
setTimeout(() => {
this.JIA(this.value)
},1000)
})
}
}
}
</script>
添加人員案例
導(dǎo)航三添加人員案例代碼,如下:
<template>
<div>
<el-select v-model="value" placeholder="請(qǐng)選擇添加人員">
<el-option
v-for="item in options"
:key="item.name"
:label="item.name"
:value="item.name">
</el-option>
</el-select>
<el-button @click="addPerson">添加</el-button>
<el-button @click="rest">還原</el-button>
<el-table
:data="filterTable"
:border='true'
style="width: 100%;marginTop:10px">
...
</el-table-column>
</el-table>
<h3 style="marginTop:10px;">此時(shí)表格總?cè)藬?shù)為:{{filterTable.length}}</h3>
<h3 style="marginTop:10px;">此時(shí)導(dǎo)航二組件的計(jì)算總和為:???</h3>
</div>
</template>
<script>
export default {
data() {
return {
value:'',
options: [...],
tableData: [...],
}
},
computed:{
filterTable(){
return this.$store.state.filterTable
},
},
mounted(){
this.initTable()
},
methods:{
initTable(){
const person = this.filterTable.length == 0 ? this.tableData : this.filterTable
this.$store.commit('RESTPERSON',person)
},
addPerson(){
const obj = {
value:this.value,
options:this.options
}
this.$store.dispatch('addPerson',obj)
},
rest(){
this.$store.dispatch('restPerson',this.tableData)
}
}
}
</script>
Vuex實(shí)現(xiàn)組件間數(shù)據(jù)共享
現(xiàn)在就算我不去介紹,大家也會(huì)用了,現(xiàn)在兩個(gè)組件所需彼此的數(shù)據(jù)已經(jīng)放到了倉(cāng)庫(kù)的state中,只需要直接獲取就行;
那我們就把兩個(gè)組件一直存在的 ”???“ 給他們獲取一下值;
計(jì)算總數(shù)組件中獲取添加人員組件中表格的人員總數(shù),如下:
...
<h3 style="marginBotton:10px;">
此時(shí)導(dǎo)航三組件內(nèi)總?cè)藬?shù)為:{{filterTable.length}}
</h3>
<h3>當(dāng)前計(jì)算的和為:{{count}}</h3>
...
computed:{
//只需要引入state中的filterTable屬性即可
...mapState(['count','filterTable'])
},
...
添加人員組件中獲取計(jì)算總數(shù)的值,如下:
...
<h3 style="marginTop:10px;">
此時(shí)表格總?cè)藬?shù)為:{{filterTable.length}}
</h3>
<h3 style="marginTop:10px;">此時(shí)導(dǎo)航二組件的計(jì)算總和為:{{count}}</h3>
...
computed:{
filterTable(){
return this.$store.state.filterTable
},
count(){
return this.$store.state.count
},
},
...
只要導(dǎo)航二計(jì)算組件中的計(jì)算總數(shù)值count發(fā)生變化,導(dǎo)航三添加人員組件內(nèi)的計(jì)算總和也隨之變化;
只要導(dǎo)航三添加人員組件表格數(shù)據(jù)發(fā)生變化,導(dǎo)航二計(jì)算總數(shù)組件內(nèi)總?cè)藬?shù)也隨之變化;如下圖:


這樣就是實(shí)現(xiàn)了組件間數(shù)據(jù)的共享,簡(jiǎn)單多了吧,你也可以像我這樣去練習(xí);
七. Vuex模塊化編碼(modules)
其實(shí)到目前為止,你已經(jīng)基本上學(xué)會(huì)了Vuex的使用了,modules只是輔助我們把Vuex模塊化,讓我們的代碼更清晰明了,便于維護(hù)和開發(fā),提高發(fā)開效率;
想一下,目前我們只是有兩個(gè)組件需要共享數(shù)組,看著沒有問題,同樣要是有十幾個(gè)組件,幾十個(gè)組件也需要共享數(shù)據(jù)呢,難道我們要把所有共享的狀態(tài)和更改的業(yè)務(wù)邏輯都寫在一個(gè)state,mutations和actions中嗎,可以想到肯定很亂,怎么辦,這就有了modules;
store模塊化
如果要使用模塊化編碼的話,store中的代碼編寫會(huì)發(fā)生變化,組件引入的api不會(huì)沒有改變,但是寫法也會(huì)有所不同;
首先我們先來使store分成模塊,目前我們有兩個(gè)組件,每個(gè)組件的state,mutations和actions都是獨(dú)立的,所以我們可以拆分兩個(gè)模塊:
在store文件夾下創(chuàng)建一個(gè)count.js文件,此文件只屬于計(jì)算總數(shù)的狀態(tài)和業(yè)務(wù)邏輯,最后記得暴漏出去,如下:
const countOptions = {
//這里添加此屬性,是為了后面使用map輔助函數(shù)簡(jiǎn)寫時(shí)可以找到該模塊
namespaced: true,
state: {
count:0,
},
getters:{
multipleCount(state){
return state.count * 10
}
},
mutations: {
JIA(state,count){
state.count += count
},
JIAN(state,count){
state.count -= count
if(state.count <= 0) state.count = 0
},
},
actions:{
asyncJia(context,value){
return new Promise((resolve,reject) => {
setTimeout(() => {
context.commit('JIA',value)
resolve()
},1000)
})
},
oddJia({commit,state},value){
if(state.count%2 !== 0){
commit('JIA',value)
}
},
},
}
export default countOptions
在store文件夾下創(chuàng)建一個(gè)addPerson.js文件,此文件只屬于添加人員的狀態(tài)和業(yè)務(wù)邏輯,最后也要暴露出去,如下:
import {message} from 'element-ui'
const addPersonOption = {
namespaced: true,
state: {
filterTable:[]
},
mutations: {
ADDPERSON(state,person){
state.filterTable.push(...person)
},
//這個(gè)重置方法本不需要,只是為了組件使用commit
//dispatch('restPerson')就可以實(shí)現(xiàn),這里為了下面講解知識(shí)點(diǎn)而用
RESTPERSON(state,person){
state.filterTable = []
state.filterTable.push(...person)
}
},
actions:{
addPerson({commit,state},obj){
var isHave = true
state.filterTable.forEach( e => {
if(e.name == obj.value) isHave = false
})
if(!isHave){
message({
message: '人員已添加!',
type: 'warning',
duration:1000
})
return
}
var person = obj.options.filter( item => {
return item.name == obj.value
})
commit('ADDPERSON',person)
},
restPerson({commit,state},value){
state.filterTable = []
commit('ADDPERSON',value)
}
},
}
export default addPersonOption
在store/index.js文件里面引入上面兩個(gè)模塊組件,掛到如下modules中,如下:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
import countOptions from './count'
import addPersonOption from './addPerson'
const store = new Vuex.Store({
modules:{
countOptions,
addPersonOption
}
})
export default store
這樣分成模塊去開發(fā),是不是,眼前一亮,清晰多啦;
模塊化的組件調(diào)用
看到這里不知道你是否發(fā)現(xiàn),我在計(jì)算總數(shù)組件里面用的都是map函數(shù)調(diào)用方法;在添加人員組件用的都是this.$store.xx的調(diào)用方法;之所以這樣做就是為了現(xiàn)在使用模塊化的時(shí)候給大家都全部介紹一下api在模塊下的引入方式;
map輔助函數(shù)
命名空間(namespaced)+ map輔助函數(shù)的簡(jiǎn)單寫法去調(diào)用,寫法如下;
mapState函數(shù)的調(diào)用改為:
computed:{
...mapState('countOptions',['count']),
...mapState('addPersonOption',['filterTable'])
},
mapGetters函數(shù)的調(diào)用改為:
computed:{
...mapGetters('countOptions',['multipleCount']),
},
mapMutations函數(shù)的調(diào)用改為:
methods:{
...mapMutations('countOptions',['JIA','JIAN']),
}
mapActions函數(shù)的調(diào)用改為:
methods:{
...mapActions('countOptions',['oddJia','asyncJia']),
}
this.$store.xxx
獲取模塊化states方法,state后面加上模塊名,如下:
computed:{
filterTable(){
return this.$store.state.addPersonOption.filterTable
},
count(){
return this.$store.state.countOptions.count
},
},
獲取模塊化mutations方法,在mutation函數(shù)名前加上 “模塊名+/”,如下:
methods:{
initTable(){
var person = this.filterTable.length == 0 ? this.tableData : this.filterTable
this.$store.commit('addPersonOption/RESTPERSON',person)
},
}
為了了解獲取模塊化getters的調(diào)用方法,我們給Vuex的addPersonOption模塊添加一個(gè)getters方法獲取表格第一個(gè)人的名字,組件調(diào)用展示,代碼如下:
getters: {
onePersonName(state){
return state.filterTable[0].name
}
},
computed:{
onePersonName(){
return this.$store.getters.['addPersonOption/onePersonName']
}
}
獲取模塊化actions方法,如下:
methods:{
addPerson(){
var obj = {
value:this.value,
options:this.options
}
this.$store.dispatch('addPersonOption/addPerson',obj)
},
rest(){
this.$store.dispatch('addPersonOption/restPerson',this.tableData)
}
}
再給大家看一下demo,修改后,功能正常使用!完美!,圖如下:


八. 總結(jié)
好了,以上就是關(guān)于Vuex的所有知識(shí)點(diǎn),因?yàn)樽罱炙⒘艘槐樯泄韫萔ue的視頻,為了練習(xí)和總結(jié),就寫了這篇將近18000字的關(guān)于Vuex博客,應(yīng)該算是很詳細(xì),很詳細(xì)啦!
到此這篇關(guān)于一篇文章帶你吃透Vuex3的狀態(tài)管理的文章就介紹到這了,更多相關(guān)Vuex3狀態(tài)管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue router-view的嵌套顯示實(shí)現(xiàn)
本文主要介紹了vue router-view嵌套顯示,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
VUE側(cè)邊導(dǎo)航欄實(shí)現(xiàn)篩選過濾的示例代碼
本文主要介紹了VUE側(cè)邊導(dǎo)航欄實(shí)現(xiàn)篩選過濾的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05
Vue全家桶實(shí)踐項(xiàng)目總結(jié)(推薦)
本篇文章主要介紹了Vue全家桶實(shí)踐項(xiàng)目總結(jié)(推薦),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11
vue router 跳轉(zhuǎn)后回到頂部的實(shí)例
今天小編就為大家分享一篇vue router 跳轉(zhuǎn)后回到頂部的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-08-08
詳解Vue如何進(jìn)行分布式事務(wù)管理以及解決方案
在分布式系統(tǒng)中,事務(wù)管理是一個(gè)非常重要的問題,所以本文將介紹一下Vue中如何進(jìn)行分布式事務(wù)管理以及分布式事務(wù)解決方案,希望對(duì)大家有所幫助2023-06-06
vue調(diào)用攝像頭進(jìn)行拍照并能保存到本地的方法
本文主要介紹了vue調(diào)用攝像頭進(jìn)行拍照并能保存到本地的方法,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
手把手教你vue實(shí)現(xiàn)動(dòng)態(tài)路由
動(dòng)態(tài)路由可以根據(jù)不同用戶登錄獲取不一樣的路由層級(jí),可隨時(shí)調(diào)配路由,下面這篇文章主要給大家介紹了關(guān)于vue實(shí)現(xiàn)動(dòng)態(tài)路由的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07
Vue中computed屬性和watch,methods的區(qū)別
本文主要介紹了Vue中computed屬性和watch,methods的區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05
vue實(shí)現(xiàn)頁面跳轉(zhuǎn)和參數(shù)傳遞的兩種方式
這篇文章主要介紹了vue頁面跳轉(zhuǎn)和參數(shù)傳遞的兩種方式,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09

