组件通信的 6 种方式

通信方式很多,网上也很好找到详细代码,这里只做简单说明

1.props/$emit[传统的父子通信]

  父组件使用props向子组件传递数据,子组件用$emit发送给父组件修改后的数据。
  父组件需要给子组件一个约定的事件名称用于接收子组件$emit传递过来的数据。
  子组件$emit的方法名就是父组件约定的事件名
  父组件:
  <Son :data="data" @getChangeData="getChange"/>
  子组件
  this.$emit('getChangeData', newData)
1
2
3
4

2.$emit/$on[自定义中央事件池]

  场景:有三个子组件互相公用同一个数据,或一个组件做出改变其他组件也要变化。
  这时候我们可以定义一个公共的bus去响应这三个子组件。
  这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件。
  巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。
  缺点是:不好控制,尤其是当组件多的时候。
// bus.js定义一个空的Vue实例
var Event = new Vue();//一般情况下我们将它抽离成一个公共的bus.js,然后分别引入到其他组件里去
exprot default Event
// 组件A
<div>
    <h3>A组件:{{name}}</h3>
    <button @click="send">将数据发送给C组件</button>
  </div>
export default {
  data() {
      return {
        name: 'tom'
      }
    },
    methods: {
      send() {
        Event.$emit('data-a', this.name);
      }
    }
}
// 组件B
<div>
    <h3>B组件:{{age}}</h3>
    <button @click="send">将数组发送给C组件</button>
  </div>
  export default {
    data() {
      return {
        age: 20
      }
    },
    methods: {
      send() {
        Event.$emit('data-b', this.age);
      }
    }
  }
  // 组件C
  <div>
    <h3>C组件:{{name}}{{age}}</h3>
  </div>
  export default {
    data() {
      return {
        name: '',
        age: ""
      }
    },
    mounted() {//在模板编译完成后执行
     Event.$on('data-a',name => {
         this.name = name;//箭头函数内部不会产生新的this,这边如果不用=>,this指代Event
     })
     Event.$on('data-b',age => {
         this.age = age;
     })
    }
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

3.vuexopen in new window

4.$attrs/$listeners[跨级通信]

跨级通信,说白了就是爷爷组件想给孙子组件传递数据,不用再使用显示的 props 一层层向下传递,直至略过父级组件。因为在一些情境里,父组件可能会修改,会删除,可能根本就用不到 props,所以,这个跨级通信解决的就是这个问题。

  • $attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。
  • $listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
  • 需要注意的是,尽管父组件可以不用显示使用 props 向下传递,但是孙子组件要想获取爷爷组件的数据,还需要父组件在孙子组件上显示传递$listeners或$attrs
  • 如果中间的父组件使用 props 接收了其中的值,那么$attrs 里就会失去该值。
  • 语法:
// 根组件中使用父组件
<Father :name="name" :age="age" @getText="getText" job="123" />
// 父组件中使用子组件
<Son v-bind="$attrs" v-on="$listeners" />
// 子组件中就可以用$attrs来使用根组件传递的属性值,
// $listeners是传递的方法
// 但是Father组件中如果用props显示接收了根组件传递的任意属性后,那么$attrs里就会失去对应的。
1
2
3
4
5
6
7

5.provide/inject

不推荐,因为它默认不是响应式的,虽然有解决办法,但是太恶心,这里简单介绍一下;

  • provide 在父级组件里提供数据
provide() {
		return {
			name: this.name || '宋宇'
		}
  }
1
2
3
4
5
  • inject 在子级组件里获取数据
inject: ["name"];
1
  • 它实际上实现了中间层不做任何处理,但是在数据响应这一块需要额外处理,第一种就是把父组件实例传入给子组件,第二就是用 Vue.observable 将数据包装后就可以了。介于还是不方便,所以不推荐使用。
  • 语法
// A 组件
<div>
      <h1>A 组件</h1>
      <button @click="() => changeColor()">改变color</button>
      <ChildrenB />
      <ChildrenC />
</div>
......
  data() {
    return {
      color: "blue"
    };
  },
  // provide() {
  //   return {
  //     theme: {
  //       color: this.color //这种方式绑定的数据并不是可响应的
  //     } // 即A组件的color变化后,组件D、E、F不会跟着变
  //   };
  // },
  provide() {
    return {
      theme: this//方法一:提供祖先组件的实例
    };
  },
  methods: {
    changeColor(color) {
      if (color) {
        this.color = color;
      } else {
        this.color = this.color === "blue" ? "red" : "blue";
      }
    }
  }
  // 方法二:使用2.6最新API Vue.observable 优化响应式 provide
  // provide() {
  //   this.theme = Vue.observable({
  //     color: "blue"
  //   });
  //   return {
  //     theme: this.theme
  //   };
  // },
  // methods: {
  //   changeColor(color) {
  //     if (color) {
  //       this.theme.color = color;
  //     } else {
  //       this.theme.color = this.theme.color === "blue" ? "red" : "blue";
  //     }
  //   }
  // }
  // F 组件
<template functional>
  <div class="border2">
    <h3 :style="{ color: injections.theme.color }">F 组件</h3>
  </div>
</template>
<script>
export default {
  inject: {
    theme: {
      //函数式组件取值不一样
      default: () => ({})
    }
  }
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

6.$parent / $children 与 ref

ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例 $parent / $children:访问父 / 子实例

Last Updated:
Contributors: websong