Vue组件之间通信

前言

  之前在学校一直在用nodom框架进行开发,对vue的了解也仅仅局限于对比二者的区别,并没有很深入的去了解。而且nodom的用法和vue的比较相似,所以上手vue是比较快的,但是其中很多原理并不熟悉,所以想总结并分享一下学到的东西。

  组件化是vue.js或者说现在的mvvm框架中一个重要的思想,也是前端发展的趋势,这其中组件间的通信又是vue数据驱动的灵魂所在。所以在这谈谈Vue2.x的组件通信,这一篇主要聊聊非Vuex的做法,下一篇文章再讲Vuex。Vue2.x已经废弃了$dispatch和$broadcast,nodom中还保留着broadcast的方法…

父子组件通信

1.父子组件,父组件向子组件传递数据

  父组件向子组件传递数据时,这是比较简单也是比较常见的一种方式,我们首先看看props,prop的值有两种一种是使用数组和对象,静态数据比较简单,作为理解就使用数组为例了,先看下代码:

<body>
  <div id="app">
    <div class="">
      父组件
    </div>
    <child message="父亲给儿子说的话:xxxxx"></child>
  </div>
</body>
  <script>
    Vue.component('child',{
      props: ['message'],
      template: '<div>{ { message }}</div>'
    });
    const app = new Vue({
      el: '#app'
    });
  </script>
</html>

  通过在props中声明数据,在子组件中添加message并赋值进行传递,需要注意的是HTML不区分大小写,在dom模板中,驼峰命名的props名称要转为短横线分隔命名。

  另外在实际开发中,很少会传递死的数据,大部分都是使用动态数据,此时我们需要使用v-bind指令来绑定父组件数据,当父组件数据发生变化时,也会传递给子组件,代码:

<div id="app">
    <div class="">
      父组件数据
      <input type="text" v-model="parentMessage">
    </div>
    child :message="parentMessage"</child>
  </div>
</body>
  <script>
    Vue.component('child',{
      props: ['message'],
      template: '<div>子组件收到的消息:{ { message }}</div>'
    });
    const app = new Vue({
      el: '#app',
      data: {
        parentMessage: ''
      }
    });
  </script>

  当然这种传递方式,不局限于字符串,object也是可以的,也是我们用的比较多的。

  另外props传值需要注意,props中声明的数据和子组件自身的data是有一定区别的,props的数据来自于父组件,而data中的数据属于组件本身,作用域也是组件本身,但是这两种数据都可以在template、computed、methods中使用。

2.父子组件,子组件向父组件传递数据,自定义事件方式

  子组件向父组件传递数据时,要用到自定义事件的方式。v-on指令除了监听dom事件,还可以用于组件间的自定义事件。子组件用$emit()方法触发事件,父组件用$on()方法进行家庭或者使用v-on进行监听自定义事件。代码:

<div id="app">
    <div class="">
      total: { { total }}
    </div>
    child @increase="getTotal"</child>
  </div>
</body>
  <script>
    Vue.component('child',{
      props: ['message'],
      template: `
      <div class="child">
        button @click="increase"+1</button>
      </div>`,
      data() {
        return {
          num: 0
        };
      },
      methods: {
        increase() {
          this.num ++;
          this.$emit('increase',this.num);
        }
      }
    });
    const app = new Vue({
      el: '#app',
      data: {
        total: 0
      },
      methods: {
        getTotal(total) {
          this.total = total;
        }
      }
    });
  </script>

  例子中,在子组件的increase方法中,$emit()方法的第一个参数是要注册的事件名称,后面的参数是要传递的数据,父组件使用v-on指令监听子组件事件,从而接收数据。

任意组件之间的通信(父子组件、兄弟组件、跨级组件)

  在vue2.x中,还有一种使用一个空的Vue实例作为中央总线(bus),也就是中介,可以实现任何组件之间的通信。like中介(虽然我挺讨厌中介的..),通信的任何组件之间,只需要通过这个中介,就可以实现通信,代码:

<div id="app">
    父组件:{ { message }}
    <component-b></component-b>
    <component-c></component-c>
  </div>
</body>
  <script>
    // 创建一个空实例 作为中央总线
    const bus = new Vue();
    Vue.component('component-b',{
      template: <button @click="handleEventB">B组件</button>,
      data() {
        return {
          componentBData: '我是B组件'
        };
      },
      methods: {
        handleEventB() {
          bus.$emit('b-message', this.componentBData);
        }
      }
    });

    Vue.component('component-c',{
      template: <button @click="handleEventC">C组件</button>,
      data() {
        return {
          componentCData: '我是C组件'
        };
      },

      methods: {
        handleEventC() {
          bus.$emit('c-message', this.componentCData);
        }
      }
    });

    const app = new Vue({
      el: '#app',
      data() {
        return {
          message: ''
        };
      },
      // 监听bus实例中的事件。
      mounted() {
        let me = this;
        bus.$on('c-message', (msg) => {
          me.message = msg;
        });
        bus.$on('b-message', (msg) => {
          me.message = msg;
        });
      }
    });
  </script>

  我们创建了一个空的vue实例bus作为中介,然后定义了两个组件b、c,两个按钮,在handle中用bus.$emit注册注册b-message的事件,并传递数据,最后在app中,在生命周期mounted的钩子函数监听这两个事件,点击按钮,就message方法发送出去,在app中就会接收到来自bus的事件,从而在回调中拿到数据。这就是中央总线的方式实现组件通信,当然这种方法不局限于父子组件,可以用于任意的组件通信方式。

最后

  还有一种父调用子和子调用父的方法实现父子通信,但是我觉得父子组件用前面提到的两种方法更加可靠,并且父调用子还可以接收,但是子调用父比较不可取,所以在这篇文章中就没有提到。

  这篇文章只提到了这几种通信方式,基本可以满足日常开发了,但是Vue2.x还提供了Vuex,把这几种方式和vuex分开,下一篇文章会谈谈Vuex。