前言
我們經(jīng)常用element-ui
做后臺管理系統(tǒng),經(jīng)常會遇到父組件給子組件傳遞數(shù)據(jù)般贼,下面一個簡單的例子,點擊按鈕,把彈框顯示變量數(shù)據(jù)通過子組件的props屬性傳遞腮介,子組件通過$emit事件監(jiān)聽把數(shù)據(jù)回傳給父組件。
父組件代碼:
<template>
<div>
<a href="javascript:;" @click="dialogshow = true">點擊</a>
<common-dialog :show.sync="dialogshow"></common-dialog>
彈框是否顯示:{{dialogshow}}
</div>
</template>
<script>
import commondialog from '@/components/dialog'
export default {
name: 'parent',
components:{
'common-dialog':commondialog
},
data () {
return {
dialogshow:false
}
},
methods:{
}
}
</script>
子組件代碼:
<template>
<el-dialog :visible.sync="elDialogShow" title="提示">
<span>這是一段彈框信息</span>
<span slot="footer" class="dialog-footer">
<el-button @click="elDialogShow = false">取 消</el-button>
<el-button type="primary" @click="elDialogShow = false">確 定</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
name:'children',
props:['show'],
computed:{
elDialogShow:{
get(){
return this.show
},
set(value){
this.$emit('update:show',value)
}
}
},
data() {
return {
};
}
}
</script>
感覺這樣挺麻煩玉锌,父組件通過設(shè)置子組件的屬性(props)來向子組件傳遞數(shù)據(jù),而父組件想獲得子組件的數(shù)據(jù)疟羹,得向子組件注冊事件主守,在子組件觸發(fā)這個事件再把數(shù)據(jù)傳遞出來禀倔。一句話總結(jié)起來就是,props 向下傳遞數(shù)據(jù)参淫,事件向上傳遞數(shù)據(jù)
救湖。
如果使用vuex,像下面這種方式就很簡單:
<!--父組件-->
<template>
<div>
<a href="javascript:;" @click="$store.state.show = true">點擊</a>
<common-dialog></common-dialog>
彈框是否顯示:{{$store.state.show}}
</div>
</template>
<!--子組件-->
<el-dialog :visible.sync="$store.state.show" title="提示">
<!--其它代碼省略-->
</el-dialog>
安裝使用vuex
安裝:
npm install vuex --save
在main.js添加配置:
import vuex from 'vuex'
Vue.use(vuex)
var store = new vuex.Store({
state:{
show:false
}
})
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
然后子組件代碼就能有效運行了涎才。
<template>
<el-dialog :visible.sync="$store.state.show" title="提示">
<span>這是一段彈框信息</span>
<span slot="footer" class="dialog-footer">
<el-button @click="$store.state.show = false">取 消</el-button>
<el-button type="primary" @click="$store.state.show = false">確 定</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
name:'children',
data() {
return {
};
}
}
</script>
modules
前面我們把store對象寫到了main.js中鞋既,為了項目的好管理,我們可以新建一個store文件夾放于src文件夾下耍铜,在store文件夾下新建index.js,代碼如下:
import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex);
export default new vuex.Store({
state:{
show:false
}
})
那么對應(yīng)的main.js得改成如下:
//vuex
import store from './store'
new Vue({
el: '#app',
router,
store,//使用store
template: '<App/>',
components: { App }
})
這樣雖然結(jié)構(gòu)看著清晰了邑闺,但是如果多個模塊,不同類型(比如用戶基本信息棕兼,用戶購物車等)放到同一個state
下面不好管理陡舅,這就用modules
。
那么store文件夾下的index.js就變成了這樣伴挚,假如我們有app與user模塊:
import Vue from 'vue'
import Vuex from 'vuex'
import app from './modules/app' //app模塊數(shù)據(jù)
import user from './modules/user' //用戶模塊數(shù)據(jù)
import getters from './getters'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
app,
user
},
getters
})
export default store
getters
這兒說明下靶衍,上面我在Store實例對象中引入了getters
,是為了后面頁面可以直接調(diào)用經(jīng)常用到的狀態(tài)信息茎芋,也就相當(dāng)于vue實例里面的computed
計算屬性颅眶,通過$store.getters.show
方式得到相關(guān)數(shù)據(jù),即通過上面的getters
引入知道田弥,是直接在store文件夾下創(chuàng)建了一個getters.js
,代碼如下:
const getters = {
show: state => state.app.show
}
export default getters
當(dāng)然這個getters
也可以放到具體的模塊中涛酗,比如放到app模塊中,官網(wǎng)的這塊代碼結(jié)構(gòu)如下:
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)
我們從index.js中看到皱蹦,我們在store文件夾下創(chuàng)建了一個modules文件夾煤杀,也就是在該文件夾下放各個需要區(qū)分的狀態(tài)模塊,比如user.js沪哺,app.js等沈自。
結(jié)構(gòu)如下圖:
上面我們通過
$store.getters.show
方式(即相當(dāng)于通過計算屬性)獲得show的值,當(dāng)然也可以通過$store.state.app.show
獲得辜妓。
app.js代碼:
export default {
state:{
show:false
}
}
mutations
在我們點擊按鈕出現(xiàn)彈框或者點擊彈框中的關(guān)閉按鈕枯途,需要修改app模塊中的show的狀態(tài),這時候就需要通過mutations進(jìn)行修改數(shù)據(jù)(同步操作)籍滴。
我們需要在app.js模塊代碼
export default {
state:{
show:false
},
mutations:{
SET_DIALOG_STATE:(state,val) => { //改變彈框是否顯示的狀態(tài)
state.show = val
}
}
}
父組件:
<template>
<div>
<a href="javascript:;" @click="$store.commit('SET_DIALOG_STATE',true)">點擊</a>
<common-dialog></common-dialog>
彈框是否顯示:{{$store.state.app.show}}
</div>
</template>
<script>
import commondialog from '@/components/dialog'
export default {
name: 'parent',
components:{
'common-dialog':commondialog
},
data () {
return {
}
},
methods:{
}
}
</script>
子組件:
<template>
<el-dialog :visible.sync="$store.getters.show" title="提示">
<span>這是一段彈框信息</span>
<span slot="footer" class="dialog-footer">
<el-button @click="$store.commit('SET_DIALOG_STATE',false)">取 消</el-button>
<el-button type="primary" @click="$store.commit('SET_DIALOG_STATE',false)">確 定</el-button>
</span>
</el-dialog>
</template>
這兒就是用到了$store.commit('SET_DIALOG_STATE',false)
來觸發(fā)mutations
中的SET_DIALOG_STATE
方法來改變狀態(tài)值
需要注意的是:
mutations
中的方法是不分模塊的酪夷,比如你在app.js中定義了SET_DIALOG_STATE
這個方法,也在user.js
中定義了SET_DIALOG_STATE
這個方法孽惰,那么在任一組件中調(diào)用$store.commit('SET_DIALOG_STATE',false)
晚岭,會執(zhí)行所有的SET_DIALOG_STATE
方法。
mutations
里的操作必須是同步的勋功。
如果在mutations
里執(zhí)行異步操作會發(fā)生什么事情 , 實際上并不會發(fā)生什么奇怪的事情 , 只是官方推薦 , 不要在 mutations
里執(zhí)行異步操作而已坦报。
actions
我們在上面的app.js中通過mutations
改變了一個狀態(tài)库说,那么如果需要改變多個狀態(tài)的值呢,需要執(zhí)行mutations中定義的多個方法(也就是說需要調(diào)用多次$store.commit()
方法)片择,那么就需要actions
那么app.js代碼如需要改成如下:
export default {
state:{
show:false
},
getters:{
showState(state){
return state.show
}
},
mutations:{
SET_DIALOG_STATE:(state,val) => { //改變彈框是否顯示的狀態(tài)
state.show = val
}
},
actions:{
set_dialog_state({commit,state},dialogVal){ //對象解構(gòu)
commit('SET_DIALOG_STATE',dialogVal)
//commit('mutations其它方法','其它方法需要改變的值')
}
//等價于下面的:
/*
set_dialog_state(context,dialogVal){
context.commit('SET_DIALOG_STATE',dialogVal)
context.commit('mutations其它方法','其它方法需要改變的值')
}
*/
}
}
那么父組件的調(diào)用方式就需要用$store.dispatch()
方法潜的,父組件代碼如下:
<div>
<a href="javascript:;" @click="$store.dispatch('set_dialog_state',true)">點擊</a>
<common-dialog></common-dialog>
彈框是否顯示:{{$store.state.app.show}}
</div>
</template>
<script>
import commondialog from '@/components/dialog'
export default {
name: 'parent',
components:{
'common-dialog':commondialog
},
data () {
return {
}
},
methods:{
}
}
</script>
子組件的代碼:
<template>
<el-dialog :visible.sync="$store.getters.show" title="提示">
<span>這是一段彈框信息</span>
<span slot="footer" class="dialog-footer">
<el-button @click="$store.dispatch('set_dialog_state',false)">取 消</el-button>
<el-button type="primary" @click="$store.dispatch('set_dialog_state',false)">確 定</el-button>
</span>
</el-dialog>
</template>
這兒就使用$store.dispatch('set_dialog_state',true)
來觸發(fā)actions
中的set_dialog_state
方法。官方推薦 , 將異步操作放在 action 中字管。
mapGetters啰挪、mapState、mapMutations嘲叔、mapActions
很多時候 , $store.state.app.show
亡呵、$store.dispatch('set_dialog_state',true)
這種寫法又長又臭 , 很不方便 , 我們沒使用 vuex 的時候 , 獲取一個狀態(tài)只需要this.show
, 執(zhí)行一個方法只需要this.set_dialog_state
就行了 , 使用 vuex 使寫法變復(fù)雜了 ?
使用 mapState、mapGetters借跪、mapActions
就變得簡單了政己。
mapGetters
比如子組件原來是這樣:
<el-dialog :visible.sync="$store.getters.show" title="提示">
<span>這是一段彈框信息</span>
<span slot="footer" class="dialog-footer">
<el-button @click="$store.dispatch('set_dialog_state',false)">取 消</el-button>
<el-button type="primary" @click="$store.dispatch('set_dialog_state',false)">確 定</el-button>
</span>
</el-dialog>
</template>
通過$store.getter.show
得到狀態(tài)值,我們也可以這樣:
<template>
<el-dialog :visible.sync="show" title="提示">
<span>這是一段彈框信息</span>
<span slot="footer" class="dialog-footer">
<el-button @click="$store.dispatch('set_dialog_state',false)">取 消</el-button>
<el-button type="primary" @click="$store.dispatch('set_dialog_state',false)">確 定</el-button>
</span>
</el-dialog>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name:'children',
data() {
return {
};
},
computed:{
...mapGetters([
'show'
])
}
}
</script>
mapGetters
輔助函數(shù)僅僅是將 store 中的 getter 映射到局部計算屬性掏愁。
當(dāng)然我們也可以給getters里面的狀態(tài)show換一個名字歇由,比如叫dialogShow,那么子組件就需要改成如下:
<template>
<el-dialog :visible.sync="dialogShow" title="提示">
<span>這是一段彈框信息</span>
<span slot="footer" class="dialog-footer">
<el-button @click="$store.dispatch('set_dialog_state',false)">取 消</el-button>
<el-button type="primary" @click="$store.dispatch('set_dialog_state',false)">確 定</el-button>
</span>
</el-dialog>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name:'children',
data() {
return {
};
},
computed:{
...mapGetters({
dialogShow:'show'
})
}
}
</script>
mapState
上面我們通過$store.getters.show
拿到狀態(tài)值,我們也可以通過$store.state.app.show
拿到值果港,那么怎樣使用mapState呢沦泌?
子組件寫法:
<template>
<el-dialog :visible.sync="show" title="提示">
<span>這是一段彈框信息</span>
<span slot="footer" class="dialog-footer">
<el-button @click="$store.dispatch('set_dialog_state',false)">取 消</el-button>
<el-button type="primary" @click="$store.dispatch('set_dialog_state',false)">確 定</el-button>
</span>
</el-dialog>
</template>
<script>
import { mapState } from 'vuex'
export default {
name:'children',
data() {
return {
};
},
computed:{
...mapState({
show:state => state.app.show
})
}
}
</script>
上面使用的是箭頭函數(shù),也可以使用常規(guī)函數(shù)辛掠,如下:
<template>
<el-dialog :visible.sync="showState" title="提示">
<span>這是一段彈框信息</span>
<span slot="footer" class="dialog-footer">
<el-button @click="$store.dispatch('set_dialog_state',false)">取 消</el-button>
<el-button type="primary" @click="$store.dispatch('set_dialog_state',false)">確 定</el-button>
</span>
</el-dialog>
</template>
<script>
import { mapState } from 'vuex'
export default {
name:'children',
data() {
return {
};
},
computed:{
...mapState({
show:state => state.app.show, //方式一 箭頭函數(shù)
showState(state){ //方式二 常規(guī)函數(shù)
return state.app.show
}
})
}
}
</script>
mapMutations
你可以在組件中使用 $store.commit('xxx')
提交 mutation谢谦,或者使用 mapMutations 輔助函數(shù)將組件中的 methods 映射為 store.commit 調(diào)用
父組件代碼如下:
<template>
<div>
<a href="javascript:;" @click="SET_DIALOG_STATE(true)">點擊</a>
<common-dialog></common-dialog>
彈框是否顯示:{{$store.state.app.show}}
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import commondialog from '@/components/dialog'
export default {
name: 'parent',
components:{
'common-dialog':commondialog
},
data () {
return {
}
},
methods:{
...mapMutations(['SET_DIALOG_STATE'])
}
}
</script>
給方法名換個名字:
<template>
<div>
<a href="javascript:;" @click="changeState(true)">點擊</a>
<common-dialog></common-dialog>
彈框是否顯示:{{$store.state.app.show}}
</div>
</template>
<script>
import { mapMutations } from 'vuex'
import commondialog from '@/components/dialog'
export default {
name: 'parent',
components:{
'common-dialog':commondialog
},
data () {
return {
}
},
methods:{
...mapMutations({
changeState:'SET_DIALOG_STATE' //改變名字
})
}
}
</script>
mapActions
你在組件中使用$store.dispatch('xxx')
分發(fā) action,或者使用 mapActions 輔助函數(shù)將組件的 methods 映射為 store.dispatch 調(diào)用
父組件代碼:
<template>
<div>
<a href="javascript:;" @click="set_dialog_state(true)">點擊</a>
<common-dialog></common-dialog>
彈框是否顯示:{{$store.state.app.show}}
</div>
</template>
<script>
import { mapActions } from 'vuex'
import commondialog from '@/components/dialog'
export default {
name: 'parent',
components:{
'common-dialog':commondialog
},
data () {
return {
}
},
methods:{
...mapActions (['set_dialog_state'])
}
}
</script>