Vue组件通信

引言

组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。一般来说,组件之间有以下几种关系:

父子、兄弟、隔代(可能隔多代),针对不同的使用场景,可以选择不同的通信方式

props/$emit

父组件需要传递给子组件的数据,以属性绑定的形式(v-bind),传递到子组件内部,拱子组件使用

子组件需要在内部定义props属性,才能使用传递过来的数据。所有的props数据都是通过父组件传递过来的

父组件向子组件传值

在父组件中引入子组件,传入相应的值,在子组件中定义props接收传过来的值

子组件向父组件传值

通过$emit发送给父组件,再由父组件监听接收

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div>
<h1>我是A组件!!</h1>
<B myname="zhangsan" @myhandle="get"></B>
</div>
</template>

<script>
import B from "./B";
export default {
components: { B },
methods: {
get(val) {
console.log("取到来自B的值:" + val); //接收$emit
},
},
};
</script>
//A组件、父组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div>
<h1>我是B组件!!{{ myname }}</h1>
<button @click="myhandle">点击</button>
</div>
</template>

<script>
export default {
data() {
return {};
},
props: ["myname"], //接收父组件
methods: {
myhandle() {
this.$emit("myhandle", "21"); //发送给父组件
},
},
};
</script>

效果

$emit

EventBus($emit,$on)

$emit和$on方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件

包括父子、兄弟、跨代关系。当项目较大时,可以选择更好的状态管理方案——Vuex。

1
2
3
var Event = new Vue();
Event.$emit(事件名,数据);
Event.$on(事件名,data=>{});

假设有三个组件,分别是A组件、B组件、C组件,C组件要获取A组件或B组件的数据

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//event.js
import Vue from 'vue'
let vm = new Vue();
export default vm;

//a.vue
<template>
<div>
<h1>我是A组件!!{{ mesg }}</h1>
<button @click="ahandle">A按钮</button>
</div>
</template>

<script>
import eventbus from "../../event";
export default {
data() {
return {
mesg: "A组件的信息",
};
},
methods: {
ahandle() {
eventbus.$emit("a的数据", this.mesg);
},
},
};
</script>
//b.vue
<template>
<div>
<h1>我是B组件!!{{ messag }}</h1>
<button @click="bhandle">B按钮</button>
</div>
</template>

<script>
import eventbus from "../../event";
export default {
data() {
return {
messag: "B组件的信息",
};
},
methods: {
bhandle() {
eventbus.$emit("B的数据", this.messag);
},
},
};
</script>
//c.vue
<template>
<div>
<h1>我是C组件!!{{ amesage }}{{ bmesage }}</h1>

<A></A>
<B></B>
</div>
</template>

<script>
import eventbus from "../../event";
import A from "./A";
import B from "./B";
export default {
data() {
return {
amesage: "",
bmesage: "",
};
},
components: {
A,
B,
},
created() {
eventbus.$on("a的数据", (mesg) => {
this.amesage = mesg;
console.log(this.amesage);
});
eventbus.$on("B的数据", (mesg) => {
this.bmesage = mesg;
console.log(this.bmesage);
});
},
};
</script>

点击按钮之后会获得信息

$attrs和$listeners

当多级嵌套组件需要传递数据时,通常使用的方法是通过Vuex。但是如果仅仅只是传递数据,而不做中间处理,用vuex有点大材小用

  • $attrs:包含了父作用域中不被props所识别(且获取)的特性绑定(class和style除外).当一个组件没有声明任何props时,则会包含所有父作用域的绑定,并且可以通过v-bind=”$attrs”传入内部组件,通常配合interitAttrs选项一起使用
  • $listeners:包含了父作用域中不含.native修饰器的v-on事件监听器,可以通过v-on=”listeners”传入内部组件
  • 常用于隔代组件通信
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
//C.vue 父组件
<template>
<div>
<h1>我是C组件!!{{ amesage }}{{ bmesage }}---{{ $attrs }}</h1>
<A :height="180" :age="21"></A>
<!-- <B></B> -->
</div>
</template>

<script>
import eventbus from "../../event";
import A from "./A";
// import B from "./B";
export default {
data() {
return {
amesage: "",
bmesage: "",
};
},
components: {
A,
// B,
},
created() {
eventbus.$on("a的数据", (mesg) => {
this.amesage = mesg;
console.log(this.amesage);
});
eventbus.$on("B的数据", (mesg) => {
this.bmesage = mesg;
console.log(this.bmesage);
});
},
};
</script>

//A.vue 子组件
<template>
<div>
<h1>我是A组件!!{{ mesg }}---{{ $attrs }}</h1>
<button @click="ahandle">A按钮</button>
<B v-bind="$attrs"></B> //也能向子组件传递
</div>
</template>

<script>
import B from "./B";
import eventbus from "../../event";
export default {
data() {
return {
mesg: "A组件的信息",
};
},
components: { B },
created() {
console.log(this.$listeners);
},
props: ["height"], //如果定义了父组件中的数据,则不会在$attrs中
methods: {
ahandle() {
eventbus.$emit("a的数据", this.mesg);
},
},
};
</script>

效果

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

provide和inject

祖先组件中通过provide来提供变量,然后在子孙组件中通过inject来注入变量。这个API主要解决了跨代组件间的通信问题

它们的应用场景主要是子组件获取上级组件的状态,跨代组件间建立了一种主动提供和依赖注入的关系

需要注意的是:provide和inject绑定并不是可响应的

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
//A.vue
<template>
<div>
<h1>我是A组件!!{{ mesg }}---{{ $attrs }}</h1>
<button @click="ahandle">A按钮</button>
<B v-bind="$attrs"></B>
</div>
</template>

<script>
import B from "./B";
import eventbus from "../../event";
export default {
data() {
return {
mesg: "A组件的",
};
},
// provide(){
// return {
// mesg:'halo'
// }
// },
provide: { name: "world" },
components: { B },
props: ["height"],
methods: {
ahandle() {
eventbus.$emit("a的数据", this.mesg);
},
},
};
</script>
//B.vue
<template>
<div>
<h1>我是B组件!!{{ messag }}</h1>
<button @click="bhandle">B按钮</button>
</div>
</template>

<script>
import eventbus from "../../event";
export default {
data() {
return {
messag: "B组件的信息",
};
},
inject: ["name"],
methods: {
bhandle() {
eventbus.$emit("B的数据", this.name);
},
},
mounted() {
console.log(this.name);
},
};
</script>

效果

$parent/$children与ref

$parent/$children:访问父/子实例,两者都是直接得到组件实例,使用后可以直接调用组件的方法或访问数据

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

弊端:两者都无法在跨代组件或兄弟组件间通信

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
//A.vue 父组件
<template>
<div>
<h1>我是A组件!!{{ mesg }}---{{ $attrs }}</h1>
<button @click="ahandle">A按钮</button>
<B v-bind="$attrs" ref="Bref"></B>
</div>
</template>

<script>
import B from "./B";
export default {
data() {
return {
mesg: "A组件的",
};
},
// provide(){
// return {
// mesg:'halo'
// }
// },
provide: { name: "world" },
components: { B },
props: ["height"],
methods: {
ahandle() {
console.log(this.$refs.Bref.messag); //获取B组件的信息
console.log(this.$children); //获取A的子组件的信息
},
},
};
</script>

//B.vue 子组件
<template>
<div>
<h1>我是B组件!!{{ messag }}</h1>
<button @click="bhandle">B按钮</button>
</div>
</template>

<script>
export default {
data() {
return {
messag: "B组件的信息",
};
},
inject: ["name"],
methods: {
bhandle() {
console.log(this.$parent.mesg);
},
},
mounted() {
console.log(this.name);
},
};
</script>

示例

vuex

state、mutations、actions、dispatch等后续做详细介绍

总结

  • 父子通信:props/$emit 、provide/inject、eventbus、$parent/$children、ref、$attrs
  • 兄弟通信:BUS、vuex
  • 跨代通信:provide/inject、$attrs/$listeners、BUS、Vuex

Vue组件通信
https://blog-theta-ten.vercel.app/2021/08/27/Vue组件通信/
作者
Chen
发布于
2021年8月27日
许可协议