vue组件通信

208 阅读2分钟

vue组件之间通信有以下三种情况

1、父子组件通信

props/$emit $refs/$parent/$children provide/inject $attrs/$listeners

2、同级组件之间通信

事件总线vm.$emit/vm.$on vuex

3、跨组件通信

vuex、 $attrs/$listeners

组件间通信共有六种方式

一、props/$emit

父组件通过props向下传递给子组件,子组件$emit事件名,父组件v-on事件来接收。 举个栗子:

// 父组件
<template>
  <div class="hello">
    {{msg}}
    <Child v-bind:msg='message' v-on:msgChange='msgChange($event)'></Child>
  </div>
</template>
<script>
import Child from './Child.vue'
export default {
  data() {
    return {
      message: 'Welcome to Child!'
    }
  },
  components:{
    Child
  }
}
</script>

// 子组件
<template>
  <div>
    <h2 @click="msgChange">{{msg}}</h2>
  </div>
</template>
<script>
export default {
  name:'Child',
  props:{ //接收父组件传递过来的值
    msg:{
      type:String,
      default:''
    }
  },
  methods: {
    msgChange(){
      this.$emit('msgChange',true)  //子组件向父组件传值
    }
  },
}
</script>

总结:父组件通过props向下传递数据给子组件。注:组件中的数据共有三种形式:data、props、computed。子组件通过events给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。

二、$emit/$on 事件总线

这方法需要通过一个Vue实例来作为中央事件总线,用它来触发事件和监听事件,可以实现任何组件间的通信。(小项目,少量事件总线通信可以使用,项目大的话推荐使用Vuex状态管理)。实现方式:

const vm = new Vue()
vm.$emit(事件名,数据)
vm.$on(事件名,data => {})

三、$attrs/$listeners

$attrs包含了父作用域中不被props所识别的特性绑定,当一个组件没有声明任何props时,所有的值可以通过v-bind='$attrs'传入内部组件中。通常配合 inheritAttrs 选项一起使用。(默认情况下父作用域的不被认作 props 的特性绑定 (attribute bindings) 将会“回退”且作为普通的 HTML 特性应用在子组件的根元素上。)

$attrs包含了父作用域中v-on实践监听器(不含.native修饰器的事件)。可以通过v-on='$listeners'传入内部组件。$listeners里存放的是父组件中绑定的非原生事件。

让我们举个栗子:

组件关系A => B => c
A是低层组件

// A组件
<template>
    <div>
        <h2>A组件</h2>
        <component-a
            :data1='data1'
            :data2='data2'
            :data3='data3'
            v-on:testChange='testChange'
        ></component-a>
    </div>
</template>
<script>
const componentA = () => import("./componentA.vue");
export default {
  components: { componentA },
  data() {
    return {
      data1: "data1",
      data2: "data2",
      data3: "data3",
    };
  },
  methods:{
      testChange(){}
  }
};
</script>
// B组件
<template>
    <div>
        <h2>B组件</h2>
        <component-c
            v-bind='$attrs'
            v-on='$listeners'
            v-on:eventChange='eventChange'
        ></component-c>
    </div>
</template>
<script>
import componentC from './componentC.vue'
export default {
    components:{componentC},
    props:['data1']
    data(){
        return{
            
        }
    },
    mounted(){
        除了props的所有参数都在$attrs上
        console.log(this.$attrs) //{data2: "data2", data3: "data3"}
        
        console.log(this.$listeners) //{testChange: ƒ}
    },
    methods:{
      eventChange(){}
  }
}
</script>
// C组件
<template>
  <div>
    <h2>C组件</h2>
    <p>C组件得到的$attrs: {{ $attrs }}</p>
  </div>
</template>
<script>
export default {
  props: {
    data2: String
  },
  mounted(){
        除了props的所有参数都在$attrs上
        console.log(this.$attrs) //{ data3: "data3"}
        A、B组件的事件会累计
        console.log(this.$listeners) //{testChange: ƒ, eventChange: ƒ}
    },
};
</script>

四、$parent/$children$ref

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

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

以上两种方式都可以直接得到组件实例,可以直接调用组件的方法或者访问数据。举个栗子:

// 父组件
<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();  // 弹窗
    }
  }
  
  子组件
  export default {
  data () {
    return {
      title: 'Vue.js'
    }
  },
  methods: {
    sayHello () {
      window.alert('Hello');
    }
  }
}
</script>

注意:以上两种方式无法在跨级组件或者兄弟组件之间通信,只能用于父子组件间通信。

五、privide 和 inject

简介:Vue2.2.0新增,允许一个祖先组件向其所有子孙后台注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终有效。也就是祖先组件通过provide来提供变量,然后在子孙组件中通过inject来注入变量。

provide/inject主要解决了跨级组件间的通信问题,使用场景:主要是子组件获取上级组件的状态,跨级组件间建立一种主动提供与依赖注入的关系。

举个栗子:

// 父组件
export default{
    provide:{
        name:'Arya'
    }
}

// 子组件
export default{
    inject: ['name'],
    mounted(){
        console.log(this.name) // Arya
    }
}

注意:provide和inject绑定并不是响应式的,然而如果你传入一个可监听的对象,其对象的属性还是可响应式的。 也就是以上name如果改变了,后代组件是不会监听到的。 但是如果我们传入的是一个name:{value:'Arya'},这个value的改变是可以监听到的。 此外我们可以传入function,例如我们常需要在子组件编辑了数据,父组件去刷新列表。

// 父组件
export default{
  provide(){
    return{
      refresh: this.refresh
    }
  },
  methods:{
    refresh(){
      获取新数据
    }
  }
}

// 子组件
export default{
    inject: ['refresh'],
    methods:{
      handler(){
        this.refresh()
      }
  }
}

怎么实现provide与inject的数据响应式(高阶组件功能,我们后面再学)

六、vuex