vue组件以及父子组件之间的通信

2,257 阅读6分钟

写在前面

最近在学vue,vue刚开始感觉还蛮简单。但是学到后面感觉着实有点绕,真的非常绕,研究组件这一块花了一下午。从一头雾水到逐渐明朗,决定写一篇博客加深自己理解。这篇文章就主要讲一下怎么运用组件,以及父组件向子组件传递数据,子组件向父组件传递数据这三部分。希望对大家对组件的来理解也有所帮助。

文章的开始

1.如何使用组件

组件主要是提高代码重用性,让代码可以重复利用。组件在使用之前需要注册组件,注册组件的方法有两种。一种是全局注册,一种是局部注册。

注册组件要在Vue实例创建前

1.1组件全局注册

顾名思义,全局就是任何地方,也就是说全局注册的组件在任何Vue的实例中都可以使用。

  • 例1
Vue.component('my-component',{
    template:'<div>注册组件</div>'
})

其中my-component是自定义组件名,可以任意命名,推荐小写减号分割的形式。

后面的大括号里内容是组件选项,在组件选项加template可以在页面中显示出组件的内容。

其内容必须被dom元素包起来,比如说上面的“注册组件”被一组div包裹起来了。

1.2组件局部注册

局部组件只在该实例的作用域下才有效,具体代码如下

  • 例2
<div id="app">
        <my-component></my-component>
    </div>
    <script>
        var child = {
            template:''<div>局部注册组件</div>''
        }
        var app = new Vue({
            el:'#app',
            components: {
                'my-comoponent':child
            }
        })
    </script>

总结:template后面的内容就是组件的内容,将会在页面中显示。

2.父组件向子组件传递数据

2.1父传子的基本用法

在将数据传输之前,我们先应该知道哪个是父组件,哪个是子组件。就拿全局的来说,其中‘<my-component></my-component>’是父组件,父组件通过prop正向传递数据给Vue.component中子组件。

props的值有两种,一种是字符串数组,用于传递数据;一种是是对象,用于对props进行验证。后者比较好理解,这里主要来讲一下字符串数组这种形式。

  • 例3
<div id="app">
        <input type="text" v-model= "parentMessage">
        <my-component :Message = "parentMessage"></my-component>
    </div>
    <script>
        Vue.component('my-component',{
            props:['message'],
            template: '<div>{{ message }}</div>'
        });
        var app = new Vue({
            el:'#app',
            data:{
                 parentMessage: ''
            }
        })
    </script>

上面的例子中,文本框中v-model与data中parentMessage绑定。当你在文本框输入信息的时候,输入的内容会绑定到data中parentMessage中。父组件中的Message传递到子组件中的props上,子组件于是获取到message这个属性,该属性就会通过template后显示出来。结果如下图所示。

2.2父传子应当注意的地方

在子组件中,当你想要改变其中数据的值得时候不能直接改变,应该将改变的过程写在data对象或者计算属性computed里面。

在JavaScript中对象和数组是引用类型,指向同一个内存空间。当props是对象和数字时候,在子组件里面改变值会影响父组件的值。当父组件有多个的时候很明显。子组件里面值改变一个,所有的父组件都会改变。而我们通常期望父组件之间是相互独立的。

这里可能会有人看到子组件的值改变,父组件也跟着改变可能会理解为子组件向父组件传递值。其实两者是不一样的。区别如下:

首先props是父组件传子组件,父组件的个数与页面中dom元素的个数一致,而子组件template后的内容决定了每一个父组件在页面中渲染的结果
举个栗子

  • 例4

例4实现的功能是点击add按钮,number值会加1。当点击按钮触发点击事件,执行增加函数,在函数里面直接munber++来改变值。确实可以完成,但是会报错并提示应该用data或computed来改变props。大家也可以复制下面的代码自己试一试。

<div id="app">
        <!-- v-bind将对象里面的东西一并传递给子组件 -->
        <my-conponent v-bind="childrenInfo"></my-conponent>
    </div>
   <script>
        var app = new Vue({
            el:'#app',
            data: {
                childrenInfo: {
                    number:0
                }
            },
            components: {
                'my-conponent':{
                    //子组件里校验,number类型是Number
                    props:{
                        number: {
                            type: Number
                        }
                    },
            
                    template:
                    '\
                    <div>\
                        <div>{{ number }}</div>\
                        <button @click="handleClick">add</button>\
                    </div>'
                    ,
                    methods: {
                        handleClick() {
                            this.number++;
                        }
                    }
                }
            }
        })
    </script>

警告如图红色部分:

既然要用data或者计算属性,那我们就在data中修改number的值。在子组件中写一个data对象,值为函数。ownNumber: this.number就将props的number赋给ownNumber,并在子组件中使用ownNumber就可以了。代码如下:

例4-2

<body>
    <div id="app">
        <!-- v-bind将对象里面的东西一并传递给子组件 -->
        <my-conponent v-bind="childrenInfo"></my-conponent>
    </div>
    <script>
        var app = new Vue({
            el:'#app',
            data: {
                childrenInfo: {
                    number:0
                }
            },
            components: {
                'my-conponent':{
                    //子组件里校验,number类型是Number
                    props:{
                        number: {
                            type: Number
                        }
                    },
                    data : function() {
                        return {
                            ownNumber: this.number
                        }
                   },
                    template:
                    '\
                    <div>\
                        <div>{{ ownNumber }}</div>\
                        <button @click="handleClick">add</button>\
                    </div>'
                    ,
                    methods: {
                        handleClick() {
                            this.ownNumber++;
                        }
                    }
                }
            }
        })
    </script>
</body>

例4-2与例4相比,有2个地方进行了更改。一个是添加了data对象,一个是number改成了ownNumber,之所以改ownNumber,是因为data对象中,number:this.number会报错number已经存在在props当中。

3.子组件向父组件传递数据

子组件通过$emit来触发事件,在父组件中能够监听到触发的这个事件去执行一个函数。通过第5个例子来详细讲解子传父的过程。

  • 例5
<div id="app">
       <p>总数:{{ total }}</p>
       <my-component 
       @increase = 'handeleGettotal'
       @reduce = 'handeleGettotal'></my-component>
   </div> 
   <script>
       Vue.component('my-component',{
           template: '\
           <div>\
               <button @click = "handleIncrease">+1</button>\
               <button @click = "handleReduce">-1</button>\
           </div>',
           data: function () {
               return {
                   counter:0
               }
           },
           methods: {
               handleIncrease :function () {
                   this.counter++;
                   //通过$emit()把改变后的counter传递给父组件
                   this.$emit('increase',this.counter);
               },
               handleReduce :function () {
                   this.counter--;
                   this.$emit('reduce',this.counter);
               },
           }
       })
       var app = new Vue({
           el:'#app',
           data:{
              total:0
           },
           methods: {
               handeleGettotal: function (total) {
                   this.total =  total;
               }
           }
       })

结果如图所示,初始的时候总数为0;当玲珑点击三次-1后结果变为-3.

该demo实现的效果是两个按钮,一个增加,一个减少。当点击按钮,触发点击事件执行handleIncrease和handleReduce函数。以handleIncrease函数为例,先在子组件中改变counter的值,子组件中通过emit来触发increase函数。emit('increase',this.counter)中increase表示触发的函数名。那么在父组件中就会有相应的increase函数。

我们可以看到父组件的@increase="handleGetTotal"中,监听到increase事件并执行handleGetTotal函数;在里面有handleGetTotal函数,$emit('increase',this.counter)里面的第二个参数this.counter传给app中handleGetTotal函数中的total。完成了子组件向父组件传递数据的过程。

总结一下,子组件通过emit触发了increase或者reduce事件,在父组件中监听到这两个事件自己在执行相应的函数来改变值。其中emit函数第一个参数就触发的事件名,后面的参数作为实参传给子组件监听到事件后要执行的函数。

文章的结束

阅读完该文章应该知道如何创建组件,以及父子组件之间是如何传递信息的。刚开始看书的玲珑都还弄不清楚哪个是父组件,哪个是子组件,就觉得特别绕,其实认真弄清楚了之后再回过头来看看并没有那么复杂。看书学习一定不要只看书还应该把例子敲一敲,细细揣摩,最后总会有所收获。