引言
组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。一般来说,组件之间有以下几种关系:
父子、兄弟、隔代(可能隔多代),针对不同的使用场景,可以选择不同的通信方式
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>
|


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