組件內(nèi)通信主要分為兩種:父子組件通信和子組件之間通信。
1.父子組件通信
- 父組件通過
props
屬性向子組件傳遞數(shù)據(jù),這點和React一樣。 - 子組件通過事件
emit
給父組件發(fā)送消息。
看一個例子:
// 組件A中使用子組件confirmDialog
<template>
...
<vc-confirmDialog :msgTitle="msgObj.title" :msgBody="msgObj.msg"
v-on:cancel="closeDialog" v-on:ok="deleteItem">
</vc-confirmDialog>
</template>
<script type="es6">
import confirmDialog from '../widget/confirmDialog.vue';
export default{
...
components: {
'vc-confirmDialog': confirmDialog //定義組件標(biāo)簽
},
}
</script>
//子組件confirmDialog
<script type="es6">
export default{
...
// 接收父組件的屬性值
props: {
msgTitle: {
type: String,
default: ''
},
msgBody: {
type: String,
default: ''
}
}
},
methods: {
cancel: function () {
// 觸發(fā)父組件屬性函數(shù)
this.$emit('cancel');
},
ok: function () {
// 觸發(fā)父組件屬性函數(shù)
this.$emit('ok');
}
}
}
</script>
父組件通過屬性綁定方式<vc-confirmDialog :msgTitle="msgObj.title" :msgBody="msgObj.msg" @cancel="closeDialog" @ok="deleteItem"></vc-confirmDialog>
傳遞了兩類屬性參數(shù)給子組件:
- 數(shù)據(jù)屬性:
msgTitle
和msgBody
是傳遞給子組件的數(shù)據(jù),子組件通過props
接收。 - 事件屬性:父組件通過
@eventName="eventFuc"
來定義接收函數(shù),一旦子組件觸發(fā)需要父組件同步更新的事件$emit("eventName")
,父組件即會調(diào)用eventFunc
,然后更新數(shù)據(jù)。
父組件可以利用ref
屬性直接訪問組件實例/DOM對象。這一點,和React一樣。
<vc-confirmDialog :msgTitle="msgObj.title" :msgBody="msgObj.msg"
@cancel="closeDialog" @ok="deleteItem" ref="dialog"></vc-confirmDialog>
...
//父組件直接調(diào)用子組件方法
this.$refs.dialog.ok();
相比Vue,React父子組件通信無需事件機(jī)制,只需要屬性傳遞即可(參考文章:React入門系列(六)組件間通信)。
2.組件間通信
我們可以定義一個空的Vue實例作為event bus,通過事件廣播emit
和事件監(jiān)聽on
來傳遞數(shù)據(jù)。
// 數(shù)據(jù)總線bus.js
import Vue from 'vue';
export default new Vue();
// 組件A中觸發(fā)事件
bus.$emit('toggleLogin', false);
// 組件B中接收事件
bus.$on('toggleLoading', (show)=>{
this.isShowLoading = show;
});
如果是大量組件交叉性復(fù)雜通信,建議用插件Vuex處理。
3.Vuex
Vuex的核心是一個全局store,其為一個容器,包含著應(yīng)用中大部分的狀態(tài)(state)。
Vuex 和單純的全局對象有以下兩點不同:
Vuex 的狀態(tài)存儲是響應(yīng)式的。當(dāng) Vue 組件從 store 中讀取狀態(tài)的時候,若 store 中的狀態(tài)發(fā)生變化,那么相應(yīng)的組件也會相應(yīng)地得到高效更新。
不能直接改變 store 中的狀態(tài)。改變 store 中的狀態(tài)的唯一途徑就是顯式地提交(commit) mutations。
(1) state
Vuex 使用 單一狀態(tài)樹 —— 用一個對象就包含了全部的應(yīng)用層級狀態(tài)。
這也意味著,每個應(yīng)用將僅僅包含一個 store 實例(可以利用modules把store細(xì)分)。
vue組件內(nèi)調(diào)用:
this.$store.state.count
也可以通過輔助函數(shù)mapState
獲取多個狀態(tài)值。
export default {
computed: mapState({
count: state => state.count,
countAliasName: 'count',
totalCount (state) {
return state.count + this.localCount
}
})
}
如果計算屬性名與state
節(jié)點屬性名稱一直,那么,可以通過數(shù)組方式更加便捷的獲取state
節(jié)點。
如果mapState
的值和其他計算屬性混合使用,那么,利用ES的對象展開運(yùn)算符吧。
computed: {
// 獲取 store.state.count
...mapState(["count"]),
others
}
(2) Getter
Getter
可以認(rèn)為是store
上的計算屬性,它的返回值會根據(jù)依賴被緩存起來,只有當(dāng)依賴值發(fā)生了變化,才會被重新計算。
state: {
todos: [
{ id: 1, text: 'abc', checked: true },
]
},
getters: {
checkedTodos: state => {
return state.todos.filter(todo => todo.checked)
}
}
組件中使用getter
如下:
computed: {
checkedTodosCount () {
return this.$store.getters.checkedTodos.length
}
}
如果組件需要批量使用多個getter
函數(shù),那么,可以通過輔助函數(shù)mapGetters
將getter
混入到computed
對象中。
export default {
computed: {
...mapGetters(['checkedTodosCount'])
}
}
(3) moutation
mutation 非常類似事件:每個 mutation 都有一個字符串的事件類型 (type) 和 一個 回調(diào)函數(shù) (handler)。
- mutation 必須是同步函數(shù)
- 提交mutation需要用
commit
函數(shù)
mutations: {
//payload就是額外的參數(shù)
increment (state, payload) {
state.count += payload.amount;
}
}
組件內(nèi)觸發(fā)事件:
// 以載荷形式分發(fā)
this.$store.commit('increment',{count: 10}); 或者
// 以對象形式分發(fā)
this.$store.commit({
type: 'increment',
count: 10
})
也可以使用輔助函數(shù)mapMutations
將多個store.commit
映射到method
對象上。
export default {
methods: {
...mapMutations([
'increment', // 等價于`this.$store.commit('increment')`
]),
}
}
(4) actions
與mutations不同之處:
- Action 提交的是 mutation。
- Action 可以包含異步操作。
- 提交Action需要用
dispatch
函數(shù)
actions: {
increment ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
組件內(nèi)觸發(fā)事件:
this.$store.dispatch('increment');
同樣的,可以利用輔助函數(shù)mapActions
將組件的methods 映射為 store.dispatch
調(diào)用。
export default {
methods: {
...mapActions([
'increment', // 等價于`this.$store.dispatch('increment')`
}
}
(5) modules
使用單一狀態(tài)樹,導(dǎo)致應(yīng)用的所有狀態(tài)集中到一個很大的對象。但是,當(dāng)應(yīng)用變得很大時,store 對象會變得臃腫不堪。
為了解決以上問題,Vuex 允許將 store 分割到模塊(module)。每個模塊擁有自己的 state,mutation,action。
// modules/index.js如下
import knowledge from './knowledge'
import common from './common'
import demo from './demo'
import help from './help'
export default {
knowledge,
common,
demo,
help
}
// index.js 如下
import modules from "./modules";
export default new Vuex.Store({
modules,
});
// 組件內(nèi)使用如下
store.state.help
store.state.demo