Vue组件之间的通信(上)

参考: https://juejin.im/post/5c77c4ae518825407505e262

本文提到三种组件之间通信的方式

  • 父子组件之间通信
    • props 和 $emit 适合直接父子关系的通信
    • $attrs 和 $listeners 可以进行爷孙之间的通信
  • 同级组件的通信
    • *事件总线的方式
const EventBus = new Vue();
Vue.prototype.$EventBus = EventBus;

方式一:props和$emit

props以单向数据流的形式可以很好的完成父子组件的通信

  • 所谓单向数据流,就是数据只能通过props由父组件流向子组件,而子组件并不能通过修改props传过的数据修改父组件的相应状态
<body>
    <div id="app"></div>
    <script src="node_modules/vue/dist/vue.min.js"></script>
    <script  type="text/javascript">
        //父组件
        Vue.component('parent',{
            data(){
                return{
                    message:'hello'
                }
            },
            template:'<div>' +
                '<p>this is parent component</p>' +
                '<child :message="message" v-on:getChildData="getChildData"></child>' +
                '</div>',
            methods:{
                getChildData(val){
                    console.log(val);
                }
            }
        });
        //子组件
        Vue.component('child',{
            template:'<div><input type="text" v-model="myMessage" @input="passData(myMessage)"/></div>',
            prop:['message'],
            data(){
                return{
                    myMessage:this.message
                }
            },
            methods:{
                passData(val){
                    this.$emit('getChildData',val);
                }
            }
        })
       new Vue({
           el:'#app',
           template:'<div><parent/></div>'s
       })
    </script>
</body>

方式二:$attrs 和 $listeners

方式一组件通信的方式只适合直接的父子组件。针对方式一的不足,提供了attrsattrs和attrs和listeners来实现直接让组件A传递消息给组件C

  • 实现组件A传消息给组件C
<body>
    <div id="app"></div>
    <script src="node_modules/vue/dist/vue.min.js"></script>
    <script  type="text/javascript">
        Vue.component('A',{
            template: '<div><p>this is parent component</p><B :messagec="messagec" :message="message" ' +
                'v-on:getCData="getCData(messagec)" v-on:getChildData="getChildData(message)"></B></div>',
            data(){
                return{
                    messagec:'hello c',
                    message:'hello'
                }
            },
            methods:{
                getChildData(val){
                    console.log('这是来自组件B的数据' + val)
                },
                getCData(val){
                    console.log('这是来自组件C的数据' + val)
                }
            }
        });
        //组件B
        Vue.component('B',{
            <!-- C组件中能直接触发 getCData 的原因在于:B组件调用 C组件时,使用 v-on 绑定了 $listeners 属性 -->
            <!-- 通过v-bind 绑定 $attrs 属性,C组件可以直接获取到 A组件中传递下来的 props(除了 B组件中 props声明的) -->
            template:'<div>' +
                '<input type="text" v-model="mymessage" @input="passData(mymessage)">' +
                '<C v-bind="$attrs" v-on="$listener"></C>' +
                '</div>',
            props:['message'],
            data(){
                return{
                    mymessage:this.message
                }
            },
            methods:{
                passData(val){
                    this.$emit('getChildData',val)
                }
            }
        });
        //组件C
        Vue.component('C',{
            template:'<div><input type="text" v-model="$attrs.messagec" @input="passCData($attrs.messagec)"></div>',
            methods:{
                passCData(val){
                    //触发父组件A中的事件
                    this.$emit('getCData',val)
                }
            }

        })
        new Vue({
            el:'#app',
            template:'<div><A/></div>'
        })

    </script>
</body>

方式三:*事件总线EventBus(适用于同级组件)

对于父子组件之间的通信,上面两种方式可以完全实现,但是对于两个组件不是父子关系,又该如何实现呢?项目规模不大,可以使用*事件总线Event

    <body>
        <div id="app"></div>
        <script src="vue.min.js"></script>
        <script>
            //EventBus 通过新建一个 Vue 事件 bus 对象,然后通过 bus.$emit 触发事件,bus.$on 监听触发的事件。
            //定义*事件总线
            const EventBus = new Vue();
            
            //将*事件总线赋值到Vue.prototype上,这样所有组件都可以访问了
            Vue.prototype.$EventBus = EventBus;  //这里主要应该是在原型链上添加属性
            //组件A
            Vue.component('A',{
                template:`
                <div>
                    <p>this is A component</p>
                    <input type="text" v-model="mymessage" @input="passData(mymessage)" />
                </div>
                `,
                data(){
                    return{
                        mymessage: 'hello brother1'
                    }
                },
                methods:{
                    passData(val){
                        this.$EventBus.$emit('globalEvent',val)
                    }
                }
            });

            //组件B
            Vue.component('B',{
                template:`
                    <div>
                        <p>This is B component</p>
                        <p>组件A传过来的数据:{{brotherMessage}}</p>
                    </div>
                `,
                data(){
                    return{
                        brotherMessage:''
                    }
                },
                mounted(){
                    this.$EventBus.$on('globalEvent', (val) => {
                        this.brotherMessage = val;
                    })
                }
            })

            new Vue({
                el: '#app',
                template:`
                    <div>
                        <A />
                        <B />
                    </div>
                `
            })
        </script>
    </body>

下篇想详细理解一下vuex再做个比较好的笔记,现在的笔记太乱啦~

上一篇:使用EventBus + Redis发布订阅模式提升业务执行性能


下一篇:EventBus的使用及实现原理