Vue 组件通信

本文由清尘发表于2020-03-24 14:58最后修改于2020-03-26属于javascript分类

主要有4种Vue组件通信方式:父子组件的通信、非父子组件的eventBus通信、利用本地缓存实现组件通信、Vuex通信

父组件向子组件通信

父组件向子组件传递数据,在这里介绍两种方式,一种需使用props,另一种要通过$parent。props默认是单向绑定:当父组件的属性变化时,将传导给子组件,但是反过来不会。这是为了防止子组件无意中修改了父组件的状态。

 <div id="app">
    <div>
      <input v-model="parentMsg">
      <br>
      <child v-bind:my-message="parentMsg"></child>
    </div>
  </div>

  <script src="https://cdn.bootcss.com/vue/2.6.11/vue.js"></script>
  <script>
    Vue.component('child', {
      props: ['myMessage'],
      template: '<span>{{myMessage}}</span>'
    })
    new Vue({
      el: '#app',
      data: {
        parentMsg: '父组件内容'
      }
    })
  </script>

props默认是单向绑定,如果需要使用双向绑定可以使用.sync显式地指定双向绑定,这使得子组件的数据修改会回传给父组件。

<my-component v-bind:my-name.sync="name" v-bind:my-age.sync="age"></my-component>

如果单次绑定,可以使用.once显式地指定单次绑定,单次绑定在建立之后不会同步之后的变化,这意味着即使父组件修改了数据,也不会传导给子组件。

<my-component v-bind:my-name.once="name" v-bind:my-age.once="age"></my-component>

(2)直接在子组件中通过this.$parent调用其父组件,但并不建议使用。

子组件向父组件通信

子组件向父组件通信也有两种方式:一种使用自定义事件,另一种使用$refs。

1.使用自定义事件

子组件向父组件通信

  <div id="container">
    <p>{{msg}}</p>
    <parent-component></parent-component>
  </div>
  
  <script src="https://cdn.bootcss.com/vue/2.6.11/vue.js"></script>
  <script>
    Vue.component("parent-component", {
      data: function () {
        return {
          sonMsg: ""
        }
      },
      methods: {
        recvMsg: function (msg) {
          this.sonMsg = msg;
        }
      },
      template: `
          <div>
            <h1>这是父组件</h1>
            <p>子组件传来的数据为:{{sonMsg}}</p>
            <hr/>
            <child-component @customEvent="recvMsg"></child-component>
          </div>
        `
    })
    Vue.component("child-component", {
      methods: {
        sendMsg: function () {
          this.$emit("customEvent", "Vue组件学习中");
        }
      },
      template: `
          <div>
            <h1>这是子组件</h1>  
            <button @click="sendMsg">senToFather</button>
          </div>
        `
    })
    new Vue({
      el: "#container",
      data: {
        msg: "Hello VueJs"
      }
    })
  </script>

使用一个自定义事件实现子组件与父组件直接的通信,其中this.$emit(“customEvent”,”Vue组件学习中”)中$emit()的意思是把事件沿着作用域链向上派送。

自定义事件进行格式化和位数限制。

  <h1>事件机制</h1>
  <div id="app">
    <currency-input v-model="price"></currency-input>
  </div>
  
  <script src="https://cdn.bootcss.com/vue/2.6.11/vue.js"></script>
  <script>
    Vue.component('currency-input', {
      template: `
          <span>$ <input ref="input" v-bind:value="value" v-on:input="updateValue($event.target.value)" /></span>
        `,
      props: ['value'],
      methods: {
        //不是直接更新值,而是使用此方法对输入值进行格式化和位数限制
        updateValue: function (value) {
          var formattedValue = value.trim().slice(0, value.indexOf('.') + 3) //删除两侧的空格,保留两位小数位
          if (formattedValue !== value) { //如果值不统一,手动覆盖以保持一致
            this.$refs.input.value = formattedValue
          }
          this.$emit('input', Number(formattedValue)) //通过input事件发出数值
        }
      }
    })

    var app = new Vue({
      el: '#app',
      data: {
        price: 10.123
      }
    })
  </script>

2.使用$refs

步骤1:在调用子组件的时候,可以制定refs属性。

<child-component refs="xiaoming"></child-component>

步骤2:通过$refs得到指定引用名称对应的组件实例。

this.$refs.xiaoming

任意组件及平行组件通信

eventBus这种通信方式针对的是非父子组件之间的通信,它的原理还是通过事件的触发和监听。但是因为是非父子组件的关系,它们需要有一个中间组件来连接。可以在根组件,也就是#app组件上定义一个所有组件都可以访问到的组件,具体方式如下。使用eventBus传递数据的3个步骤如下。实例代码如示例所示。

(1)创建一个Vue实例,作为事件绑定触发的公共属性。
(2)在发送方的组件触发自定义事件。

//1.创建一个Vue实例
bus:new Vue()
//2.在子组件触发自定义的事件 $emit() ———— 把事件沿着作用域链向上派送
bus.$emit('changeMsgEvent','需要传递的数据')
//3.在接收组件监听事件,接收数据
mounted(){
  bus.$on('changeMsgEvent',function(msg){
    //msg 是通过事件传递来的数据
  })
}

任意组件及平行组件通信。

  <h1>组件之间的通信</h1>
  <p>有时候两个组件也需要通信(非父子关系)。在简单的场景下,可以使用一个空的Vue实例作为中央事件总线</p>
  <div id="app">
    <h2>组件A:向总线上报事件</h2>
    <my-component-a v-bind:counter="total"></my-component-a>
    <h2>组件B:通过总线监听相关事件</h2>
    <my-component-b></my-component-b>
  </div>
  <script src="https://cdn.bootcss.com/vue/2.6.11/vue.js"></script>
  <script>
    var bus = new Vue();
    Vue.component('my-component-a', {
      template: `<div><p>组件A</p><hr/> <button v-on:click="doClick">{{counter}}</button><hr/></div>`,
      data: function () {
        return { counter: 1 }
      },
      methods: {
        doClick: function () {
          this.counter++;
          bus.$emit('btn-click', this.counter)
        }
      }
    })
    Vue.component('my-component-b', {
      template: `<div><p>组件B</p><hr/>计数器:{{counter}} <hr/></div>`,
      data: function () {
        return {
          counter: 0
        }
      },
      methods: {
        foo: function (value) {
          console.log(value);
          this.counter = value;
        }
      },
      created: function () {
        bus.$on('btn-click', this.foo);
      }
    })
    var app = new Vue({
      el: '#app',
      data: {
        total: 0
      },
      methods: {
        doChildClick: function () {
          this.total += 1
        }
      }
    })
  </script>