有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问根组件。
官方指出:
1)父组件访问子组件:使用$children或$refs;
2)子组件访问父组件:使用$parent。
1、父组件访问子组件:$children
1)this.$children是一个数组,它包含所有子组件对象;
2)我们这里通过一个遍历,取出所有子组件的message状态。
<div id="app"> <parent-cpn>parent-cpn>div><template id="parentCpn"> <child-cpn1>child-cpn1> <child-cpn2>child-cpn2> <button @click="showMessage">显示所有子组件信息<button>template><template id="childCpn1"> <h2>我是子组件一<h2>template><template id="childCpn2"> <h2>我是子组件二<h2>template>Vue.component("parent-cpn",{ template:"#parentCpn", methods:{ showMessage(){ for(let i=0; i<this.$children.length; i++){ console.log(this.$children[i].message); } } })
说明:这里的message没有写出来,message分别在两个子组件的data里进行定义。这里通过this.$children遍历访问了两个子组件的message。
2、父组件访问子组件:$refs
上面使用的$children存在缺陷:
1)$children是数组类型,访问其中的子组件必须通过索引值;
2)但是当子组件过多时,我们需要拿到其中一个子组件往往不能确定它的索引值。
有时候,我们想明确获取其中一个特定的子组件,这个时候就可以使用$refs。
$refs的使用:
$refs和ref指令是一起使用的。
1)首先,我们通过ref给某一个子组件绑定一个特定的ID;
2)其次,通过this.$refs.ID就可以访问到该子组件了。
说明:上面所说的ID对应child1和child2,访问的时候通过this.$refs.ID的形式。
3、子组件访问父组件:$parent
如果我们想在子组件中直接访问父组件,可以通过$parent。
注意事项:
尽管在Vue开发中,允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。
子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。如果我们将子组件放在另外一个组件(父组件)之内,很可能该父组件没有对应的属性,往往会引起问题。
另外,更不应该做的是通过$parent直接修改父组件的状态,因为这样做的话父组件中的状态将变得飘忽不定,很不利于我们的调试和维护。
<div id="app"> <parent-cpn>parent-cpn>div><template id="parentCpn"> <child-cpn>child-cpn>template><template id="childCpn"> <button @click="showParent">显示父组件信息<button>template>Vue.component("parent-cpn",{ template:"#parentCpn", data(){ return {message:"我是父组件"} }, components:{ "child-cpn",{ template:"#childCpn", methods:{ showParent(){ console.log(this.$parent.message); } } } }})
说明:通过this.$parent访问父组件的message。
4、非父子组件之间的通信:中央事件总线和Vuex
上面说的都是父子组件间的通信,那如果是非父子组件关系呢?
非父子组件关系包括多个层级的组件关系,也包括兄弟组件的关系。在Vue1.x的时候,可以通过$dispatch和$broadcast完成:
1)$dispatch用于向上级派发事件;
2)$broadcast用于向下级广播事件。
但是在Vue2.x都被取消了,在Vue2.x中的方案是通过中央事件总线Bus,也就是一个中介来完成。通常是将Bus挂载到Vue根实例对象中使用,或者将Bus抽离成Bus.js的形式通过引入的方式使用。
方式一:挂载到Vue根对象中。在子组件中通过this.$root.Bus.$emit()触发,通过this.$root.Bus.$on()监听。
import Vue from 'vue';const Bus = new Vue();var app= new Vue({ el:'#app', data:{ Bus }})
方式二:将Bus抽离成Bus.js。同样是在一个组件中通过this.$root.Bus.$emit()触发,通过this.$root.Bus.$on()监听。
Bus.jsimport Vue from 'vue';const Bus = new Vue();export default Bus//组件1import Bus from './Bus';export default { data() { return { ...... } }, methods: { ...... Bus.$emit('log', 120); }}//组件二import Bus from './Bus';export default { data() { return { ...... } }, mounted() { Bus.$on('log', content => { console.log(content); }); }}
但是这种方案比直接使用Vuex状态管理逊色很多,并且Vuex提供了更多好用的功能,所以这里暂且不讨论事件总线Bus这种方案,后续会进行Vuex状态管理的说明。