4、组件间通讯

父子组件

image-20210803143831123

子组件访问父组件属性和方法

方式一:props(父组件传参给子组件)

在子组件中声明props,属性可以是数组(元素必须是字符串)、也可以是对象,声明的每一个属性值都可以作为子组件的属性使用

props也是双向绑定的

注意:不建议子组件直接修改props里面的值,由父组件传值,子组件只使用,如果需要修改,在data中重新声明一个变量来接收prop

父组件Father.vue

<template>
  <div>
      <!-- 父组件向子组件传参 -->
      <Child :msg="hello"/>
  </div>
</template>

<script>
import Child from './Child.vue';

export default {
    name: "Father",
    components: {
        Child
    },
    data() {
        return {
            hello: 'Hello World',
        };
    },
}
</script>

子组件Child.vue

<template>
  <div>
      {{msg}}
  </div>
</template>

<script>
export default {
    name: "Child",
    // 子组件进行接收
    props: ['msg']
    /**
    props还可以是一个对象,可以对传入子组件的数据进行验证,例如字符串和数组
        props:{
            类型验证
            parentcolor:String,
            msg:Array,
            类型、默认值、必填项
            book:{
                type:Array,
                default:["200","201"],
                required:true
            }
        }
    */
}
</script>

方式二:

<template id="temp1">
    <h1>
        <!-- 通过this.$parent.属性名访问父组件的属性值、方法 -->
        <font :color="this.$parent.parentColor">test</font>
    </h1>
</template>

父组件访问子组件的属性、方法

注意:在vue中的ref属性,相当于原生html的id,给子组件设置一个id,然后this.$refs.id可以进行调用,返回该组件的实例对象;也可以用在原生html标签上,返回该标签的Dom对象

子组件Child.vue

<template>
</template>

<script>
export default {
    name: "Child",
    data() {
        return {
            msg: 'Hello World',
        };
    },
    methods: {
        say(){
            alert(this.msg);
        }
    }
}
</script>

父组件Father.vue

<template>
  <div>
      <!-- 在子组件上绑定ref唯一标识 -->
      <Child ref="ch1"/>
      <button @click="runSay">click</button>
  </div>
</template>

<script>
import Child from './Child.vue';

export default {
    name: "Father",
    components: {
        Child
    },
    methods: {
        runSay(){
            // 利用 this.$ref 调用子组件方法
            this.$refs.ch1.say();
            // 利用 this.$ref 访问子组件data
            console.log(this.$refs.ch1.msg);
        }
    }
}
</script>

自定义事件

父组件给子组件绑定一个事件,子组件可以通过触发这个事件来调用父组件的回调,达到通讯的作用

this.$emit("自身上的事件",方法形参)

父组件Father.vue

<template>
  <div>
      <!-- 在子组件上绑定自定义事件 -->
      <Child ref="ch" @runShowMsg="showMsg"/>
      <!-- !!!如果需要在子组件上绑定原生dom事件,需要使用native -->
      <Child ref="ch" @click.native="showMsg"/>
  </div>
</template>

<script>
import Child from './Child.vue';

export default {
    name: "Father",
    components: {
        Child
    },
    methods: {
        showMsg(msg){
            alert(msg);
        }
    },
    mounted(){
        //!!!也可以通过获取子组件的组件实例对象,直接在实例对象上绑定事件
        //this.$refs.ch.$on("runShowMsg",this.showMsg); //绑定自定义事件
        //this.$refs.ch.$once(); //绑定只可触发一次的自定义事件
    }
}
</script>

子组件Child.vue

<template>
    <button @click="cl">click</button>
</template>
<script>
export default {
    name: "Child",
    methods: {
        cl(){
            // 触发自身的事件
            this.$emit('runShowMsg', "Hello World");
            
            //也可以使用组件实例对象的$off函数解绑事件
            //this.$off('runShowMsg') //解绑一个事件
            //this.$off(['event1','event2']) //解绑多个事件
            //this.$off() //解绑s
        }
    }
}
</script>

全局事件总线

可以实现任意组件间进行通讯

main.js中安装全局事件总线

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

// 原理:在Vue原型对象上绑定一个空的组件,组件实例对象都支持$on、$once、$off等方法操作自定义事件
// const Bus = Vue.extend({})
// Vue.prototype.$bus = new Bus()

new Vue({
    render: h => h(App),
    beforeCreate(){
        //进阶写法:在组件还未创建前,将当前Vue实例对象,作为全局事件总线使用
        Vue.prototype.$bus = this;
    }
}).$mount("#app")

接收方组件

注意:在组件接收方组件销毁之前,最好使用钩子beforeDestroy()删除声明的自定义事件,以免事件重复命名

<template>
  
</template>

<script>
export default {
    name: "Comp2",
    methods: {
        getMsg(msg){
            alert(msg)
        }
    },
    mounted () {
        //接收方,在全局事件总线上绑定一个事件和回调
        this.$bus.$on("msgToComp2",this.getMsg)
    },
    beforeDestroy () {
        //组件销毁前,删除绑定在全局事件总线上的事件
        this.$bus.$off("msgToComp2");
    }
}
</script>

发送方组件

<template>
  <button @click="sendMsg">click</button>
</template>

<script>
export default {
    name: "Comp1",
    methods: {
        sendMsg(){
            // 发送方,触发全局事件总线的事件,并传参
            this.$bus.$emit("msgToComp2","Hello World");
        }
    }
}
</script>

消息订阅与发布

使用这个功能,需要引入第三方库,例如pubsub-js,该库不依赖与vue,可以在其他js中使用

cnpm i pubsub-js

订阅方

<template>
  <button @click="sendMsg">click</button>
</template>

<script>
import pubsub from 'pubsub-js';
export default {
    name: "Comp1",
    methods: {
        sendMsg(){
            // 发送方,调用pubsub.publish(消息名,接收回调函数的形参)发送消息
            pubsub.publish("msg1","Hello World")
        }
    }
}
</script>

发布方

<template>
  
</template>

<script>
import pubsub from 'pubsub-js';
export default {
    name: "Comp2",
    methods: {
        //注意:回调函数有两个参数,第一个是消息名,第二个才是消息
        getMsg(msgName,msg){
            alert(msg)
        }
    },
    mounted () {
        //接收方,使用pubsub.subscribe(消息名,回调函数)订阅,并且返回订阅id
        this.pubId = pubsub.subscribe("msg1",this.getMsg);
    },
    beforeDestroy () {
        //组件销毁前,使用订阅id取消订阅
        pubsub.unsubscribe(this.pubId);
    }
}
</script>