组件通信的 6 种方式
通信方式很多,网上也很好找到详细代码,这里只做简单说明
1.props/$emit[传统的父子通信]
父组件使用props向子组件传递数据,子组件用$emit发送给父组件修改后的数据。
父组件需要给子组件一个约定的事件名称用于接收子组件$emit传递过来的数据。
子组件$emit的方法名就是父组件约定的事件名
父组件:
<Son :data="data" @getChangeData="getChange"/>
子组件
this.$emit('getChangeData', newData)
1
2
3
4
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
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
vuexopen in new window
3.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
2
3
4
5
6
7
5.provide/inject
不推荐,因为它默认不是响应式的,虽然有解决办法,但是太恶心,这里简单介绍一下;
- provide 在父级组件里提供数据
provide() {
return {
name: this.name || '宋宇'
}
}
1
2
3
4
5
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
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:访问父 / 子实例