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