vue的七种交互通信

200 阅读3分钟

vue有多种通信方式。

1.父子组件用props。 2.非父子组件用Event Bus。 3.项目复杂,用Vuex状态管理。

1. props和$emit

父子组件通信的方式,通过props参数和$emit事件建立通信。

props是父组件向子组件传递的参数,$emit是子组件向父组件通信触发的事件。

// 父组件
<div>
 <child :Father_value="Father_value" @getValue="getvalue"></child>
</div>
<script>
 export default{
     data(){
         return{
             Father_value:'hello,我是父组件定义的data'
         }
     },
     methods:{
         getvalue(child_val){
             console.log(child_val)//我是子组件传给你的值哦。
         }
     }
 }
</srcipt>
// 子组件
<div @click="post"> {{Father_value}}</div>
<!-- hello,我是父组件定义的data -->
<script>
 export default{
    props:['Father_value'],
    methods:{
        post(){
            this.$emit('getValue','我是子组件传给你的值哦。')
        }
    }
 }
</srcipt>
  • 子组件可以修改父组件的值吗?

可以。使用修饰符.sync

// 父组件
<div>
 <child :Father_value.sync="Father_value"></child>
</div>
<script>
 export default{
     data(){
         return{
             Father_value:'hello,我是父组件定义的data'
         }
     }
 }
</srcipt>
// 子组件
<div> {{Father_value}}</div>
<div @click.stop="changefATHERVal"></div>
<!-- hello,我是父组件定义的data -->
<script>
 export default{
    props:['Father_value'],
    methods:{
        changefATHERVal(){
            this.$emit('update:Father_value','更新它')
        }
    }
 }
</srcipt>

2. 中央事件总线Bus

父子、兄弟、跨级的通信方式。

1.使用一个空的Vue实例作为事件总线

2.通过该空Vue实例来监听和触发事件

3.bus.$emit触发事件,bus.$on监听触发的事件。

// 空vue实例
import Vue from 'vue'
export default new Vue()
<!--引入组件A,B-->
<comA></comA>
<comB></comB>
<script>
 export default{
     components:{
         comA,
         comB
     }
 }
</script>

A组件

<button @click="sendMsg">告诉组件B</button>
<script>
 import bus from './bus'
 export default{
     methods:{
         sendMsg(){
             bus.$emit('send','你好,b组件')
         }
     }
 }
</script>

B组件

<div>{{msg}}</div>
<script>
 import bus from './bus'
 export default{
    data(){
        return{
            msg:'start'
        }
    }
    created(){
       bus.$on('send',data=>{
           this.msg = data
       })
     }
 }
</script>

3. 依赖注入:provied和inject

子孙组件通信,允许一个祖先组件向其所有子孙组件中注入一个依赖,不论层次有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。

父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。

可用于vue刷新页面。

父组件:

<div>
 <child></child>
</div>
<script>
 export default{
   data(){
       return{
           isRouterAlive:true
       }
   },
     provide(){
         return{
             testData:'我是父组件的值',
             reload:this.reload
         }
     },
     methods:{
         reload(){
             this.isRouterAlive = false
             this.$nextTick(()=>{
                 this.isRouterAlive = true
             })
         }
     }
 }
</script>

子组件:

<div>我是子组件,来自父组件的消息:{{testData}}</div>
<!-- hello,我是父组件定义的data -->
<script>
 export default{
    inject:['reload','testData'],
    methods:{
        update(){
            this.reload()
        }
    }
 }
</srcipt>

4. vuex

所有组件共享状态。

另一篇文中详细介绍:Vuex的使用详解

5. $parent/$childrenref

ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。

$parent / $children:访问父 / 子实例

需要注意的是:这两种都是直接得到组件实例,无法在跨级或兄弟间通信。使用后可以直接调用组件的方法或访问数据。我们先来看个用 ref来访问组件的例子

子组件:

// component-a 子组件
export default {
  data () {
    return {
      title: 'Vue.js'
    }
  },
  methods: {
    sayHello () {
      window.alert('Hello');
    }
  }
}
<template>
  <component-a ref="comA"></component-a>
</template>
<script>
  export default {
    mounted () {
      const comA = this.$refs.comA;
      console.log(comA.title);  // Vue.js
      comA.sayHello();  // 弹窗
    }
  }
</script>

6. $attrs$listeners

跨级通信,子孙组件通信,多级组件嵌套通信。

  • $attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。

  • $listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件

父组件:

<template>
  <div>
    <h2>浪里行舟</h2>
    <child-com1
      :foo="foo"
      :boo="boo"
      :coo="coo"
      :doo="doo"
      title="前端工匠"
    ></child-com1>
  </div>
</template>
<script>
const childCom1 = () => import("./childCom1.vue");
export default {
  components: { childCom1 },
  data() {
    return {
      foo: "Javascript",
      boo: "Html",
      coo: "CSS",
      doo: "Vue"
    };
  }
};
</script>

子组件:

<template class="border">
  <div>
    <p>foo: {{ foo }}</p>
    <p>childCom1的$attrs: {{ $attrs }}</p>
    <child-com2 v-bind="$attrs"></child-com2>
  </div>
</template>
<script>
const childCom2 = () => import("./childCom2.vue");
export default {
  components: {
    childCom2
  },
  inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
  props: {
    foo: String // foo作为props属性绑定
  },
  created() {
    console.log(this.$attrs); // { "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "前端工匠" }
  }
};
</script>

孙组件:


<template>
  <div class="border">
    <p>childCom3: {{ $attrs }}</p>
  </div>
</template>
<script>
export default {
  props: {
    coo: String,
    title: String
  },
  created() {
    console.log(this.$attrs); // {"doo": "Vue"}
  }
};
</script>

$attrs表示没有继承数据的对象,格式为{属性名:属性值}。Vue2.4提供了$attrs , $listeners来传递数据与事件,跨级组件之间的通讯变得更简单。

简单来说:$attrs$listeners 是两个对象,$attrs 里存放的是父组件中绑定的非 Props 属性,$listeners里存放的是父组件中绑定的非原生事件。

7. v-model

父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,在子组件中通过this.$emit(‘input',val)自动修改v-model绑定的值。

引申提问

怎么访问到子组件的实例或者子元素?

可以通过this.$refs

在组件中怎么访问到根实例?

可以通过this.$root

如何在子组件中访问父组件的实例?

this.$parent拿到父组件实例

this.$children拿到子组件实例(数组)