Vue组件通信方式总结

一、为什么要学习组件的通信?
  • 1、组件实例的作用域是相互独立的。本身组件之间数据和方法是不能交流的
  • 2、但是对vue来说,组件之间的消息传递是非常重要的
  • 3、组件可以有一下几种关系
    在这里插入图片描述

如上图所示,A 和 B、B 和 C、B 和 D 都是父子关系,C 和 D 是兄弟关系,A 和 C 是隔代关系(可能隔多代)。

  • 所以组件之间通信就有:父子之间的通信、兄弟之间的通信、……
  • 针对不同的使用场景。如何选择有效的通信方式?
二、通信方式总结
2.1、方式一:props/$emit
props
  • 父组件向子组件传递数据是通过prop传递 A—B
  • 实现有三步:在代码中有解释
  • 代码:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="js/vue-2.4.0.js"></script>
    <style>
        .parent{
            width: 500px;
            height: 500px;
            background: skyblue;
        }
          .child{
              width: 300px;
              height: 300px;
              background: pink;
          }
    </style>
</head>
<body>
<div id="parent" :class="['parent']">
    <h1>我是父亲</h1>
    {{msg}}
<!--    调用组件-->
<!--    继承家产第一步先:通过属性添加有个继承接口-->
    <child :parent="msg"></child>
</div>
<template id="child">
    <div :class="['child']" >
        <h4>我是儿子</h4>
        {{msg}}
        <br><br><br>
<!--        //第三步可以使用继承下的数据了-->
       我继承的家产: {{parent}}
    </div>
</template>
<script>
    //定义组件
    Vue.component('child',{
        template:'#child',
        data:function () {
            return {
                msg:'我是儿子,这是我的数据'
            }
        },
        //继承家产第二步:继承后存到自己名下
        props:['parent']

    })
    new Vue({
        el:'#parent',
        data:{
            msg:'我是老爹,这是我的家产'
        },
        methods:{

        }
    })
</script>
</body>
</html>

在这里插入图片描述

$emit
  • 子组件向父组件传递数据是通过$emit触发事件来实现的B----A
  • 也是三步代码中有解释
  • 代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="js/vue-2.4.0.js"></script>
    <style>
        .parent{
            width: 500px;
            height: 500px;
            background: skyblue;
        }
          .child{
              width: 300px;
              height: 300px;
              background: pink;
          }
    </style>
</head>
<body>
<div id="parent" :class="['parent']">
    <h1>我是父亲</h1>
    {{msg}}
    <input v-model="son">
<!--    调用组件-->
<!--    老子要用小子的家产第二步:-->
    <child :parent="msg" @toparent="getSonData"></child>
</div>
<template id="child">
    <div :class="['child']" >
        <h4>我是儿子</h4>
        {{msg}}
       <button @click="toparent1" >孝敬长辈</button>
    </div>
</template>
<script>
    //定义组件
    Vue.component('child',{
        template:'#child',
        data:function () {
            return {
                msg:'我是儿子,这是我的数据',
                parent1:'这是我孝敬长辈的'
            }
        },
        //继承家产第二步:继承后存到自己名下
        props:['parent'],
    
        methods: {
//  老子要用小子的家产第三步

            toparent1(){
                this.$emit('toparent',this.parent1);
            }
        }

    })
    new Vue({
        el:'#parent',
        data:{
            msg:'我是老爹,这是我的家产',
            son:'123'
        },
        methods:{
           //老子要用小子的家产第一步:需要给父组件定义一个方法
            getSonData(son){
                this.son=son;
                console.log('继承的:'+this.son);
            }
        }
    })
</script>
</body>
</html>

在这里插入图片描述

总结:子组件继承是通过添加属性而父子间继承子组件的数据是通过事件events,实际上就是子组件把自己的数据发送到父组件
2.2、方式2-emit/emit/emit/on中央事件总线
  • 上面两种方式处理的都是父子组件之间的数据传递,而如果两个组件不是父子关系呢?
  • 种情况下可以使用中央事件总线的方式。
  • 这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案 vuex。
  • 具体实现

1、var Event=new Vue();
2、 Event.emit(事件名,数据);起自定义事件3、Event.emit(事件名,数据); 起自定义事件 3、 Event.emit(,);3Event.on(事件名,data => {
console.log(data)//上面传值
});接收自定义数据

  • 代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="js/vue-2.4.0.js"></script>
</head>
<body>
<div id="itany">
    <my-a></my-a>
    <my-b></my-b>
    <my-c></my-c>
</div>
<!--定义几个组件-->
<template id="a">
    <div>
        <h3>A组件:{{name}}</h3>
        <button @click="send">将数据发送给C组件</button>
    </div>
</template>
<template id="b">
    <div>
        <h3>B组件:{{age}}</h3>
        <button @click="send">将数组发送给C组件</button>
    </div>
</template>
<template id="c">
    <div>
        <h3>C组件:{{name}},{{age}}</h3>
    </div>
</template>
<script>
//第一步:定义一个空的Vue实例,
var Event=new Vue();
//第二步:定义组件
var A = {
    template: '#a',
    data() {
        return {
            name: 'tom'
        }
    },
    methods: {
        send() {
            Event.$emit('data-a', this.name);
        }
    }
}
var B = {
    template: '#b',
    data() {
        return {
            age: 20
        }
    },
    methods: {
        send() {
            Event.$emit('data-b', this.age);
        }
    }
}
var C = {
    template: '#c',
    data() {
        return {
            name: '',
            age: ""
        }
    },
    mounted() {//在模板编译完成后执行
        Event.$on('data-a', name => {
            this.name = name;//箭头函数内部不会产生新的this,这边如果不用=>,this指代Event
        })
        Event.$on('data-b', age => {
            this.age = age;
        })
    }
}

        var vm = new Vue({
            el: '#itany',
            components: {
                'my-a': A,
                'my-b': B,
                'my-c': C
            }
        });
</script>
</body>
</html>

在这里插入图片描述

  • 案例
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="js/vue-2.4.0.js"></script>
</head>
<body>
<div id="app">

    <my-addbtn :count="count" ></my-addbtn>
    {{count}}
    <my-removebtn :count="count" ></my-removebtn>
</div>
<script>
    var busVm=new Vue();
var vm=new Vue({
    el:'#app',
    data:{
    count:0
    },
    mounted:function(){
        busVm.$on('change',function (num) {
            this.count=num;
        }.bind(this));
    },
    components:{
        'my-addbtn':{
            template:'<button @click=" setCount">+</button>',
            props:['count'],
            methods:{
                setCount:function () {
              busVm.$emit('change',this.count+1);
                }
            }
        },
        'my-removebtn':{
            template:'<button @click=" setCount">-</button>',
            props:['count'],
            methods:{
                setCount:function () {
                    busVm.$emit('change',this.count- 1);
                }
            }
        }
    }
})
</script>
</body>
</html>

在这里插入图片描述

2.3、方式三、attrs/attrs/attrs/listeners实现跨多级的组件嵌套关系
  • attrs是存放父组件数据但不包括在props中的容器
  • listeners是存放父组件方法的容器
  • 主要用途:用在父组件传递数据给子组件或者孙组件
  • A 组件与 C 组件之间的通信
    在这里插入图片描述
  • A-----B----C
A的组件:中有A的数据和方法,并且A组件中嵌套B的组件,B的组件继承了A组件的数据和方法通过v-bind和v-on
Vue.component('A',{
    template:`
        <div :class="['A']">


            <B :messagec="messagec" :message="message" v-on:getCData="getCData" v-on:getChildData="getChildData(message)"></B>
        </div>
    `,//B中有A的数据和事件:数据通过v-bind传递  事件通过v-on传递
    data(){
        return {
            message:'A',
            messagec:'A---C' //传递给c组件的数据

        }
    },
    methods:{
        getChildData(val){
            console.log('这是来自B组件的数据')
        },
        //执行C子组件触发的事件
        getCData(val){
            console.log(val);
            console.log("这是来自C组件的数据:"+val)
        }
    }
})
B组件
  • B组件中包含C组件 C组件有一个参数通过v-bind把B接收A的数据传过去
  • 和v-on把事件传递过去
 Vue.component('B',{
     data(){
         return {
             mymessage:this.message
         }
     },
     template:`
         <div :class="['B']">
             <p>我是B组件</p>
             <input type="text" v-model="mymessage" @input="passData(mymessage)">
             <C v-bind="$attrs" v-on="$listeners"></C>

         </div>
`,//通过刚B获取了A的数据和方法,并没有给B的props中添加---
     props:['message'],//得到父组件传递过来的数据
     methods:{
         passData(val){
             //触发父组件中的事件
             this.$emit('getChildData',val)
             console.log(this.$attrs);
         }
     },
     mounted(){
         console.log(this.$attrs);//这是装入数据的容器
         console.log(this.$listeners);//这是装入方法的容器
     }
 });

这个是在B组件打印的数据计算mounted中的–这个是把props给注释了
在这里插入图片描述

C组件
  • 由B组件传递过来的attrs和attrs和attrslisteners
  • C就可以用了
Vue.component('C',{
    template:`
        <div :class="['C']">
             <p>我是C组件</p>

            <input type="text" v-model="$attrs.messagec" @input="passCData($attrs.messagec)">
            <p>{{$attrs.messagec}}</p>
         </div>
    `,

    methods:{
        passCData(val){
            //触发父组件A中的事件
            this.$emit('getCData',val)

        }
    },
    mounted(){
        console.log(this.$attrs);//这是装入数据的容器
        console.log(this.$listeners);//这是装入方法的容器
    }

});

在这里插入图片描述

var app=new Vue({
    el:'#app',
    template:`
        <div>
            <A></A>
        </div>
    `
});
2.4、方式四、Vuex
2.5、方式五、provide/inject
  • 父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。
  • 不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。
  • 而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。
  • 代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="js/vue-2.4.0.js"></script>
</head>
<body>
<div id="app"></div>
<script>
    Vue.component('child',{
        inject:['for'],//得到父组件传递过来的数据---数组的形式
        data(){
            return {
                mymessage:this.for
            }
        },
        template:`
            <div>
                <input type="text" v-model="mymessage">
            </div>`

    })
    Vue.component('parent',{
        template:`
            <div>
                <p>this is parent compoent!</p>
                <child></child>
            </div>
        `,
        //第一步:父组件来注册变量
        provide:{
            for:'test'
        },
        data(){
            return {
                message:'hello'
            }
        }
    })
    var app=new Vue({
        el:'#app',
        template:`
            <div>
                <parent></parent>
            </div>
        `
    })
</script>
</body>
</html>

在这里插入图片描述

2.6、方式六、$ parent/$ children/$refs
  • refs:首先你的给子组件做标记。demo:<firstchildref="one"></firstchild>然后在父组件中,通过this.refs:首先你的给子组件做标记。demo :<firstchild ref="one"></firstchild> 然后在父组件中,通过this.refs:demo<firstchildref="one"></firstchild>this.refs.one就可以访问了这个自组件了,包括访问自组件的data里面的数据,调用它的函数

  • $parent为当前组件树的根实例

  • $children为当前组件的直接子组件,是一个无序的数组

1)组件只能一个根节点

2)可以在自组件中使用this.$parent.属性值,或者函数

3)在父组件中可以使用this.refs.组件的标记访问子组件,或者this.refs.组件的标记 访问子组件,或者this.refs.访this.children[i].属性,,访问子组件的

4)你需要注意this的指向

2.7、方式七、v-model
  • 父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,在子组件中通过this.$emit(‘input’,val)自动修改v-model绑定的值
Vue.component('child',{
    props:{
        value:String, //v-model会自动传递一个字段为value的prop属性
    },
    data(){
        return {
            mymessage:this.value
        }
    },
    methods:{
        changeValue(){
            this.$emit('input',this.mymessage);//通过如此调用可以改变父组件上v-model绑定的值
        }
    },
mounted(){
    console.log(this.value);//hello
},

    template:`
        <div>
            <input type="text" v-model="mymessage" @change="changeValue">
        </div>`
})
Vue.component('parent',{
    template:`
        <div>
        <p>this is parent compoent!</p>
        <p>{{message}}</p>
<child v-model="message"></child>
    </div>
        `,
    data(){
        return {
            message:'hello'
        }
    }
})
var app=new Vue({
    el:'#app',
    template:`
    <div>
    <parent></parent>
    </div>
        `
})

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值