React vs Vue 之 Vue 组件通信

组件通信无外乎以下几种情况:

  1. 父子通信
  2. 子向父通信
  3. 同级通信
  4. 跨层级通信
  5. 任意组件

1. 父子通信

props

父子通信是最常见的通信方式,父组件通过props将数据传递到子组件,所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Vue.component("child", {
props: {
name: String,
age: Number
},
template: "<div>name: {{name}}, age: {{age}}</div>"
});
new Vue({
el: "#app",
template: '<child :name="name" :age="age"></child>',
data: {
name: "小明",
age: 3
}
});

非 props

一个非 prop 特性是指传向一个组件,但是该组件并没有相应 prop 定义的特性。

非 props 特性会自动添加到组件的根元素上,除非手动禁止。
live demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Vue.component("child-inner", {
props: {
name: String,
age: Number
},
template: "<div class='child-inner'> {{name}}, age: {{age}}</div>"
});
Vue.component("child", {
props: {
name: String,
age: Number
},
template:
'<div class="child"> <child-inner :name="name" :age="age"></child-inner></div>'
});
new Vue({
el: "#app",
template: ' <child notProps="hello" :name="name" :age="age"></child>',
data: {
name: "小明",
age: 3
}
});

生成的 html 结构如下,可以看到非 props 属性挂载到了编译好的 html 模板的最外层元素上。

如果想禁止集成这些非 props 属性,需要手动设置

1
2
3
4
Vue.component("my-component", {
inheritAttrs: false
// ...
});

2. 子向父通信

在 vue 中一般通过 $emit 自定义事件,抛出事件到父组件中进行通信。

1
2
// 子组件
this.$emit("change", value);
1
// 父组件 <child @change="onChange" />

3. 同级通信

同级通信方式,需要将数据传递给父组件,父组件再将数据传递给另一个子组件。这个就不再赘述。

1
2
3
4
<div>
<child1 :age="age" @change="onChangeAge" />
<child2 :age="age" />
</div>

4. 跨层级通信

与 React 的 Context API 类似,Vue 也提供跨层级的通信方式 provide / inject

live demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Vue.component("child-inner", {
inject: ["foo"],
created() {
console.log(this.foo);
},
template: "<div>{{foo}}</div>"
});

Vue.component("child", {
inject: ["foo"],
created() {
console.log(this.foo);
},
template: "<div>{{foo}}inner: <child-inner /></div>"
});

new Vue({
el: "#app",
provide: {
foo: "bar"
},
data: {},
template: "<div><child /></div>"
});

5. 任意组件

任意两个组件,这两个组件可能非嵌套关系,他们的通信需要依赖事件来处理。

在 React 中我们依赖了 events 包,在 Vue 中,Vue 本身提供事件方法。

1
2
3
<!-- template -->
<div id="app1"></div>
<div id="app2"></div>
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
const Bus = new Vue();

const app1 = new Vue({
el: "#app1",

data: {
msg: "hello"
},

methods: {
sendMsg: function(msg) {
Bus.$emit("msg", msg);
}
},
created() {
Bus.$on("msg", e => {
this.msg = e;
});
},
beforeDestroy() {
Bus.$off("msg");
},
template: "<div>{{msg}}, app1</div>"
});

new Vue({
el: "#app2",

data: {
msg: "hello"
},
created() {
Bus.$on("msg", e => {
this.msg = e;
});
},
beforeDestroy() {
Bus.$off("msg");
},
template: "<div>{{msg}}, app2</div>"
});

app1.sendMsg("hello world!");