组件通信
前端在用 Vue 框架进行开发时,使用核心思想之一的组件式开发。
在源生JS中,我们熟悉数据之间的通信,页面之间的通信;在这里,Vue 通过组件通信来实现所有互动。
这里介绍父子组件、兄弟组件的一种常用而便捷的通信方式,同时在其中不经意间也有一些小小的坑。
父子组件
父子组件之 props
Prop 的单向数据流。
父组件给子组件传值
父组件 App.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<template>
<div id="app">
{{father}}
<Childone :child="father" />
</div>
</template>
<script>
import Childone from "./components/childone"
export default {
name: 'app',
data(){
return{
father:100
}
},
components:{
Childone
}
}
</script>
|
子组件 childone.vue
1
2
3
4
5
6
7
8
9
10
11
|
<template>
<div class="one">
{{child}}
</div>
</template>
<script>
export default {
props:['child'],
}
</script>
|
在父组件的 template 中对子组件的引用,通过 :child="father"
来将 father 的值赋予 子组件中的 child。
子组件申请改变 props
在 Vue 文档中:
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
使用 $emit
和 $event
来实现子组件向父组件“申请”修改 props 并同步更新父组件的需求。
- 子组件通过在一个事件(比如 click 等)中使用
$emit
来触发另外一个事件并传出一个参数
- 父组件定义对该事件的监听,来响应子组件的变化
- 父组件可以通过
$emit
来获取子组件传出的参数
子组件 childone.vue
1
2
3
4
5
6
7
8
9
10
11
12
|
<template>
<div class="one">
{{child}}
<button @click="$emit('update:child',child-100)">减100</button>
</div>
</template>
<script>
export default {
props:['child'],
}
</script>
|
触发的这个事件名叫做 update:child
,传出的参数为 child-100
。
父组件对这个事件进行监听,并且做出响应 father=$event
,可以说 $event === child-100
。
父组件 App.vue
1
2
3
4
5
6
7
8
9
10
|
<template>
<div id="app">
{{father}}
<Childone :child="father" v-on:update:child="father=$event" />
</div>
</template>
<script>
...
</script>
|
父子组件之间的通信实现。
切记
子组件 $emit('update:child',child-100)
在事件后传入的应该是一个值,而不是一个其他表达式等,如 child = child -100
是不可取的。
兄弟组件
使用 Vue 中的 中央事件总线(eventBus)
, 用 eventBus.$emit
来触发事件, 用 eventBus.$on
来监听事件。
其实这种方式可以用于任何的父子组件通信以及同级别组件(兄弟组件)的通信。
main.js
1
2
3
4
|
let eventBus = new Vue()
Vue.prototype.$eventBus = eventBus
new Vue(...).$mount(...)
|
子组件1 childone.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<template>
<div class="one">
{{word}}
<button @click="putWord">告诉兄弟</button>
</div>
</template>
<script>
export default {
data(){
return{
word:'Hello'
}
},
methods:{
putWord(){
this.$eventHub.$emit('put',this.word)
}
}
}
</script>
|
子组件2 childtwo.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<template>
<div class="two">
{{wordNew}}
</div>
</template>
<script>
export default {
data() {
return {
wordNew: ''
};
},
created(){
this.$eventHub.$on("put", dataFrom => {
this.wordNew = dataFrom;
});
}
};
</script>
|
在子组件1中点击 button 即可将数据传给子组件2。
一个坑
注意:定义 eventBus 的位置,一定要在创建 Vue 实例之前,否则 子组件2在创建和挂载之前的原型中将不会有 eventBus 存在。但是子组件1的功能却正常,因为在你触发点击事件时,已经使用新的原型。
子组件2的 eventBus.$on 将会变为 undefined。
当你在创建一个 Vue实例 之后才定义 eventBus。
main.js
1
2
3
4
|
new Vue(...).$mount(...)
let eventBus = new Vue()
Vue.prototype.$eventBus = eventBus
|
解决一
子组件2可以通过 watch 来触发使用新的原型。
子组件2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<script>
export default {
data() {
return {
wordNew: ''
};
},
watch: {
wordNew: {
handler() {
this.$eventHub.$on("put", dataFrom => {
this.wordNew = dataFrom;
});
},
deep: true,
immediate: true
}
}
};
</script>
|
解决二
或强行对 子组件2 产生一个更新,在 updated 中再使用 eventBus。
子组件2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<script>
export default {
data() {
return {
wordNew: 'wordNew'
};
},
created() {
setTimeout(() => {
this.wordNew = ''
}, 0);
},
updated() {
this.$eventHub.$on("put", dataFrom => {
this.wordNew = dataFrom;
});
}
};
</script>
|
这个坑也是我自己在实现用 eventBus 实现组件通信时发现的,并且用两种不同的尝试“解决”了这个有趣的坑。
:)