方法一、props、$emit
props、$emit是最常用的父子组件通信方式,父<->子
props:父组件向子组件传值
首先在子组件内定义字段接收从父组件传的值
<template>
<div class="B">
<p>B</p>
<p>{{msg}}</p>
</div>
</template>
<script>
export default {
name: "B",
props: {
msg: String //在定义字段接收从父组件传的值
}
}
然后在父组件内使用子组件定义的字段msg向子组件传值
<template>
<div class="A">
<p>A</p>
<B msg="我是A组件传的数据"/>
</div>
</template>
<script>
import B from './B.vue'
export default {
components: { B },
name: 'A',
}
</script>
$emit:子组件向父组件传值
$emit函数解释
子组件通过$emit可以调用父组件的自定义函数,其接受两个参数,$emit(eventName, [...args])
event:事件名
[...args]: 需要传递的数据
在父组件定义事件message,接收子组件的事件调用
<template>
<div class="A">
<p>A</p>
<p>{{msgFromB}}</p>
<B @message="receive"/>
</div>
</template>
<script>
import B from './B.vue'
export default {
components: { B },
name: 'A',
data() {
return {
msgFromB: "",
}
},
methods: {
receive(msg){
this.msgFromB = msg;
}
},
}
</script>
子组件通过$emit("message", value)调用父组件message绑定的函数,进行传值
<template>
<div class="B">
<p>B</p>
<button @click="sendMsg">向A组件传值</button>
</div>
</template>
<script>
export default {
name: "B",
props: {
msg: String
},
methods: {
sendMsg(){
this.$emit("message", "我是B组件传的值")
}
},
}
</script>
方法二、EventBus
EventBus又名事件总线,通过创建空vue实例的方式实现各组件间的通信,该方式可以实现任意组件间的通信
首先创建eventBus.js文件,导出vue实例,代码如下:
import Vue from "vue";
export const eventBus = new Vue();
然后在需要使用消息传递的组件内引入evenBus,通过使用eventBus的$emit来传递消息
<template>
<div class="B">
<p>B</p>
<button @click="sendMsg">通过EventBus传值</button>
</div>
</template>
<script>
import {EventBus} from '../eventBus.js'//引入EventBus
export default {
name: "B",
props: {
msg: String
},
methods: {
sendMsg(){
EventBus.$emit("message", "我是EventBus传的值")
}
},
}
</script>
在需要接收事件的组件内引入EventBus,使用$on来监听发送的事件
<template>
<div class="A">
<p>A</p>
<p>{{msgFromBus}}</p>
</div>
</template>
<script>
import {EventBus} from '../eventBus.js'//引入EventBus
export default {
name: 'A',
data() {
return {
msgFromBus: "",
}
},
mounted() {
EventBus.$on("message", (msg) => {
this.msgFromBus = msg;
})
},
}
</script>
这样就轻松实现了任意组件间的通信,在使用EventBus的时候需要注意,在页面销毁的时候要使用EventBus.$off("message")移除监听,避免反复监听。
还可以把EventBus配置成全局变量使用,直接在main.js内设置全局变量即可
Vue.prototype.$bus = new Vue();
使用方法
//发送消息
this.$bus.$emit("message", msg)
//监听消息
this.$bus.$on("message", (msg) => {
})
//移除监听
this.$bus.$off("message")
方法三、Vuex
直接上 Vuex官网 吧,里面有详细的安装与使用介绍。需要注意的一点是vuex的数据并不会保存到本地,如果页面刷新,数据也会丢失,所以如果有需要,可以配合localStorage使用。
方法四、$attrs/$listeners
这两个对象一般在多级组件嵌套时使用,在vue2.4以上版本支持,下图为vue官网对这两个属性的解释
举例说明:比如现在有A、B、C三个组件。
组件A:
<template>
<div class="A">
<p>A</p>
<B msgB="BBB" msgC="CCC" @listener="onListener"></B>
</div>
</template>
<script>
import B from './B.vue'
export default {
components: {B},
methods: {
onListener(){
console.log("listener事件被调用");
}
},
}
</script>
组件B:
<template>
<div class="B">
<p>B</p>
<C v-bind="$attrs" v-on="$listeners"></C>
</div>
</template>
<script>
import C from './C.vue'
export default {
components: {C},
props: {
msgB: String
},
mounted() {
console.log("组件B $attrs", this.$attrs);
console.log("组件B $listeners", this.$listeners);
},
}
</script>
组件C
<template>
<div class="C">
<p>C</p>
<button @click="onClick">回调listener</button>
</div>
</template>
<script>
export default {
props: {
msgC: String
},
mounted() {
console.log("组件C msgC", this.msgC);
},
methods: {
onClick(){
this.$emit("listener");
}
},
}
</script>
然后我们看输出
可以看到,组件B内输入的$attrs有msgC,也就是上面描述的父作用域中不作为props被识别的attribute绑定(msgB被识别了,所以在$attrs里面没有),而在组件C内调用this.$emit("listener")能被组件A监听到也对应了官方文档$listeners的描述:v-on="$listener"可以把父作用域中的(不含.native修饰器)的所有监听事件传递到内部组件。这两个属性在封装组件时非常的试用。
方法五、provider/inject
先上官方解读
简单来说就是一个祖先组件通过 provide 提供一个变量,后代所有子孙组件都可以通过 inject 注入这个变量来使用。
举个简单的例子
//祖先组件A提供state
<template>
<div class="A">
<B></B>
</div>
</template>
<script>
import B from './B.vue'
export default {
components: {B},
provide: {
state: 1
}
}
</script>
//子组件注入state
<template>
<div class="B">
<p>B</p>
<C></C>
</div>
</template>
<script>
import C from './C.vue'
export default {
components: {C},
inject: ["state"],
mounted() {
console.log("state=", this.state); //输出state=1
}
}
</script>
//孙子组件注入state
<template>
<div class="C">
<p>C</p>
</div>
</template>
<script>
export default {
inject: ["state"],
mounted() {
console.log("C组件 state", this.state);//输出state=1
},
}
</script>
运行代码看输出,可以看到子孙组件都输出了祖先组件提供的state=1的值。
官网provide 和 inject 绑定并不是可响应的。这是刻意为之的。然而,如果你传入了一个可监听的对象,那么其对象的 property 还是可响应的。
下面我们把数据改成可响应的
//祖先组件
<template>
<div class="A">
<p>A</p>
<button @click="onclick">修改provide的值</button>
<B></B>
</div>
</template>
<script>
import B from './B.vue'
export default {
components: {B},
provide() {
return {
obj: this.obj
}
},
data() {
return {
obj: {
state: 1
}
}
},
methods: {
onclick(){
this.obj.state += 1;
}
},
}
</script>
//子组件
<template>
<div class="B">
<p>B</p>
<p>{{this.obj.state}}</p> <!-- 值可响应 -->
</div>
</template>
<script>
export default {
inject: ["obj"],
}
</script>
上面例子运行后在父组件内点击按钮【修改provide的值】,子组件内使用了obj的地方也会跟着改变。
方法六、$parent/$children、ref/$refs
这几个都是通过获取组件实例的方式来通信,拿到组件后就可以直接组件的数据和函数。
$parent:当前组件的父组件实例(有可能为空,即当前组件是顶级组件的时候)
$children:当前实例的直接子组件,返回值是个数组。使用的时候需要注意 $children 并不保证顺序,也不是响应式的。
这个比较简单,就不上代码举例了,this.$parent, this.$children即可获取到实例。
ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例:
$refs:一个对象,持有注册过 ref 的所有 DOM 元素和组件实例。
这两个配合使用,下面举例说明
//父组件
<template>
<div class="A">
<button @click="onclick">访问B组件的数据</button>
<!--1、通过 ref 注册组件引用信息, 名字自定义 -->
<B ref="refB"></B>
</div>
</template>
<script>
import B from './B.vue'
export default {
components: {B},
methods: {
onclick(){
//2、通过$refs[ref名字]获取组件实例
const bCom = this.$refs["refB"];
bCom.num += 1;//直接调用变量修改值
bCom.add();//调用函数
}
},
}
</script>
//子组件
<template>
<div class="B">
<p>B</p>
<p>{{num}}</p>
</div>
</template>
<script>
export default {
data() {
return {
num: 1,
}
},
methods: {
add(){
this.num += 1;
}
},
}
</script>