1. Vue Components' Communication by props and event
1.1. Parent to Child -- props
props 用于 parent component 发送到 child component。
App.vue:
<template>
<app-child v-bind:msg="message"></app-child>
</template>
<script>
import Child from './components/Child.vue'
export default {
components: {
'app-child': Child
},
data() {
return {
message: 'hello'
}
}
}
</script>
Child.vue:
<template>
<div>{{ msg }}</div>
</template>
<script>
export default {
// props: ['msg'],
props: {
'msg': {
type: String,
required: true
}
},
data() {
return {
}
}
}
</script>
需要注意的点:
- Parent 使用
v-bind
进行 props 传递, Child 用 props 接收 - Child props 也可以完成 validation
1.2. primitive and reference types
- primitive types: string, number, boolean
- reference types: array, object
假设 Parent 传递的 props 是一个 array,这个 array 的 props 被多个 Child 使用,则在一个 Child 中进行修改 array,其实是修改了 data source,因为是通过引用传递的。这样其他 Child 的数据也会被改变。对数据共享来说是好事。Children 之间没有对 reference types 进行隔离。
如果 Parent 传递的 props 是 primitive types,一个 Child 修改只影响自身,其他 Child 不会被修改。如果希望数据同步,有这两做法:
- 被修改的 Child emit event,Parent 订阅这个 event,回调函数中修改属性,这样所以 Children 重新渲染
- 不通过 Parent,希望 Child2 直接关注 Child1 的状态,使用 event bus
1.3. Child to Parent -- Event
App.vue: 父级通过 v-on:子组件 emit 名字 = 回调函数
实现对子组件事件的订阅。回调函数进行数据处理。
<template>
<div>
<app-header v-bind:title="title" v-on:changeTitle="updateTitle($event)"></app-header>
<app-footer v-bind:title="title"></app-footer>
</div>
</template>
<script>
import Header from './components/Header.vue';
import Footer from './components/Footer.vue';
export default {
components: {
'app-header': Header,
'app-footer': Footer
},
data () {
return {
title: 'Vue Wizards'
}
},
methods: {
updateTitle: function(updatedTitle){
this.title = updatedTitle;
}
}
}
</script>
Header.vue: 子组件在某个函数中 emit 某个自定义事件和数据 -- this.$emit('changeTitle', 'Vue Ninjas');
<template>
<h1 v-on:click="changeTitle">{{ title }}</h1>
</template>
<script>
export default {
props: {
title: {
type: String,
required: true
}
},
data(){
return{ }
},
methods: {
changeTitle: function(){
this.$emit('changeTitle', 'Vue Ninjas');
}
}
}
</script>
Footer.vue: 另一个子组件因为父级 title props 改变而重新渲染,数据也就改变了。
<template>
<footer>
<p>Copyright 2017 {{ title }}</p>
</footer>
</template>
<script>
export default {
props: {
title: {
type: String,
required: true
}
},
data(){
return{
}
}
}
</script>
1.4. Event bus
Event bus is a vue instance.
main.js:
export const bus = new Vue();
Header.vue: 引起变化的子组件。
<template>
<h1 v-on:click="changeTitle">{{ title }}</h1>
</template>
<script>
// 引入 event bus
import { bus } from '../main';
export default {
props: {
title: {
type: String,
required: true
}
},
data(){
return{
}
},
methods: {
changeTitle: function(){
// 修改自己的 title 属性
this.title = 'Vue Ninjas';
// 向 bus emit 事件
bus.$emit('titleChanged', 'Vue Ninjas');
}
}
}
</script>
Footer.vue: 关心 Header 的子组件。
<template>
<footer>
<p>Copyright 2017 {{ title }}</p>
</footer>
</template>
<script>
// 引入 event bus
import { bus } from '../main';
export default {
props: {
title: {
type: String,
required: true
}
},
data(){
return{
}
},
// 生命周期钩子,创建时候 bus 就订阅事件
created(){
bus.$on('titleChanged', (data) => {
this.title = data;
});
}
}
</script>
1.5. 结合 react 的思考
Vue 的组件间通信简单说就是 props 和 event emit。子级 emit,父级订阅之并在回调函数中修改 props 数据,子级重新渲染。
react 父级向子级通信也是通过 props。子级向父级:子级某个函数调用 setState 去修改父级的 state,而这个 setState 也是通过 props 传递给子级的。react 可能需要把多个子级都需要的数据提取到父级 state 中。
Vue 子组件之间可以通过 Event bus 直接进行通信。React 单项数据流不推荐这种做法,但可以通过 context API 进行跨多个 level 组件间的 props 传输。