Vue的组件通信
组件之间传值、调用函数的方法
方法 | 使用场景 |
---|---|
props/$emit | 父向子传递数据是通过 props(只是传值),子向父是通过$emit给父函数传参并触发 |
$refs | 父给子的数据赋值或触发子的函数方法,可传值也可以调用函数 |
$emit/$on | 可实现任何组件间的通信,包括父子、兄弟、跨级 |
provide/inject | 适用于多层嵌套的组件之间,一个祖先组件向其所有子孙后代注入依赖,在起上下游关系成立的时间里始终生效,但不是很推荐这种方法 |
$attrs/$listeners | 适用于跨级推荐,仅传递数据 |
总结:前面三种方法较为常用,可以解决绝大部分需求,后面两种不太常用。
VUE组件间的各类关系
在 Vue 中,组件之间可以有不同的关系,包括父子关系、兄弟关系和祖先后代关系。这些关系可以通过组件的嵌套和组件之间的通信来建立和维护。下面是一些常见的组件关系:
父子关系(Parent-Child Relationship):一个组件可以包含另一个组件,被包含的组件称为子组件,包含子组件的组件称为父组件。父组件可以通过 props 向子组件传递数据,子组件可以通过事件向父组件发送消息。
兄弟关系(Sibling Relationship):同一个父组件下的多个子组件之间可以是兄弟关系。兄弟组件之间可以通过共享父组件传递的数据或事件来通信。
祖先后代关系(Ancestor-Descendant Relationship):一个组件可以作为另一个组件的祖先组件或后代组件。祖先组件是指包含当前组件的所有嵌套层次上的组件,后代组件是指当前组件的所有嵌套层次下的组件。祖先组件和后代组件之间的通信可以通过 props 和事件进行。![组件关系](https://img-blog.csdnimg.cn/direct/2654e5183e3c4063a570a3649d6ff759.jpeg#pic_center)
props/$emit
父组件向子组件传值props
props有数组和对象的两种写法:
#数组,简单的用法
props:['parentValue']
#对象,更高级的用法,有多种配置项,下面只列举了常用的三个
props:{
parentValue:{
// 多个可能的类型
type: [String, Number],
required: true,
// 自定义验证函数
validator: (value) => {
return parseInt(value) >= 0
}
}
在父组件 A.vue:
<template>
<div>
<h1>父组件 A</h1>
//使用v-on绑定要传的值parentdata,message是子组件要接受的变量名
<Child-component :message="parentdata"></Child-component>
</div>
</template>
<script>
//导入子组件
import ChildComponent from './B.vue';
export default {
name: 'ParentComponent',
//定义子组件
components: {
ChildComponent,
},
data() {
return {
//定义值parentdata
parentdata: 'Hello from Parent',
};
},
};
</script>
在子组件B.vue中:
<template>
<div>
<h2>子组件 B</h2>
<p>{{ receivedData }}</p>
</div>
</template>
<script>
export default {
name: 'ChildComponent',
//接收值
props: {
//接受方法
message: {
type: String,
required: true,
},
},
data(){
return{
user_defined:this.message
}
}
computed: {
receivedData() {
return this.message;
},
},
};
</script>
组件中的数据共有三种形式:data、props、computed
值得注意的是,props的传值是单向的:当父组件改变props的值,子组件使用props接收的值也会发生改变(用data去接受不会改变,用computed接收会改变,具体原理请自行思考),而子组件的值发生改变,是不会影响父组件的值的。
子组件触发父组件的方法$emit
要在子组件 B.vue 中使用 $emit
方法触发父组件 A.vue 的事件,可以按照以下步骤进行操作:
在父组件 A.vue:
<template>
<div>
<h1>父组件 A</h1>
<child-component @customEvent="handleCustomEvent"></child-component>
</div>
</template>
<script>
import ChildComponent from './B.vue';
export default {
name: 'ParentComponent',
components: {
ChildComponent,
},
methods: {
handleCustomEvent(payload) {
console.log('Received payload from child:', payload);
// 处理从子组件接收到的数据
},
},
};
</script>
在子组件 B.vue:
<template>
<div>
<h2>子组件 B</h2>
<button @click="triggerCustomEvent">触发事件</button>
</div>
</template>
<script>
export default {
name: 'ChildComponent',
methods: {
triggerCustomEvent() {
const payload = 'Hello from Child';
this.$emit('customEvent', payload);
},
},
};
</script>
在上述示例中,子组件 B.vue 中的按钮绑定了 @click
事件,并在事件处理程序中使用 this.$emit
方法触发了名为 customEvent
的自定义事件,并传递了一个 payload(有效载荷)作为参数。
在父组件 A.vue 中,使用 @customEvent
监听子组件触发的 customEvent
事件,并在相应的事件处理程序 handleCustomEvent
中接收传递的 payload 数据。
当子组件 B.vue 的按钮被点击时,triggerCustomEvent
方法将被调用,触发 customEvent
事件并传递 payload 数据给父组件 A.vue。父组件 A.vue 中的事件处理程序 handleCustomEvent
将接收到该 payload 数据,并进行相应的处理。
这样,通过 $emit
方法和自定义事件,可以在子组件中触发事件,并将数据传递给父组件进行处理。
$refs
父组件A使用 $refs 来向子组件 B.vue 传递值和调用方法时,可以按照以下步骤进行操作:
在父组件 A.vue:
<template>
<div>
<h1>父组件 A</h1>
<child-component ref="childRef"></child-component>
<button @click="passValueAndCallMethod">传递值和调用方法</button>
</div>
</template>
<script>
import ChildComponent from './B.vue';
export default {
name: 'ParentComponent',
components: {
ChildComponent,
},
methods: {
passValueAndCallMethod() {
const valueToPass = 'Hello from Parent';
const childComponent = this.$refs.childRef;
// 传递值
childComponent.valueFromParent = valueToPass;
// 调用方法
childComponent.childMethod();
},
},
};
</script>
在子组件 B.vue:
<template>
<div>
<h2>子组件 B</h2>
<p>{{ valueFromParent }}</p>
</div>
</template>
<script>
export default {
name: 'ChildComponent',
data() {
return {
valueFromParent: '',
};
},
methods: {
childMethod() {
console.log('Child method called');
// 子组件方法的实现
},
},
};
</script>
在上述示例中,父组件 A.vue 中使用 ref=“childRef” 将子组件 B.vue 标记为 childRef 的引用。
在父组件 A.vue 的 passValueAndCallMethod 方法中,我们可以通过 this.$refs.childRef 获取到子组件 B.vue 的实例,并直接访问子组件的属性和方法。
通过 childComponent.valueFromParent = valueToPass,我们将值 valueToPass 从父组件传递给子组件的 valueFromParent 属性。
通过 childComponent.childMethod(),我们调用子组件的 childMethod 方法。
这样,通过 $refs,我们可以在父组件中获取到子组件的实例,并直接操作子组件的属性和方法,实现数据传递和方法调用的功能。
需要注意的是,当使用 $refs 时,确保在子组件被渲染后才能访问 $refs,因此最好在适当的生命周期钩子函数(如 mounted)或在合适的时机访问 $refs。
$emit/$on
刚刚我们的例子只是父传子,子传父进行传数据,当遇到组件嵌套比较深或比较复杂的情况(有递归组件时),我们可以使用事件总线 (EventBus) 。
在我的另一篇博客里开发可无限回复的评论区(评论的递归组件)用到了事件总线。
首先,创建一个新的 Vue 实例作为事件总线,在主文件(通常是 bus.js)中:
// bus.js
import Vue from 'vue';
// 创建事件总线实例
export const eventBus = new Vue();
然后,假设我们有三个组件 A.vue、B.vue 和 C.vue,其中 B.vue 嵌套在 A.vue 中,而 C.vue 嵌套在 B.vue 中。我们想要在 C.vue 中向 A.vue 传递值。可以按照以下步骤进行操作:
在 A.vue 中:
<template>
<div>
<h1>父组件 A</h1>
<B-component></B-component>
</div>
</template>
<script>
import BComponent from './B.vue';
import { eventBus } from './bus.js';
export default {
name: 'AComponent',
components: {
BComponent,
},
mounted() {
// 监听来自 C.vue 的事件
eventBus.$on('valueFromC', this.handleValueFromC);
},
methods: {
handleValueFromC(value) {
console.log('Received value from C:', value);
// 处理从 C.vue 接收到的数据
},
},
};
</script>
在 B.vue 中:
<template>
<div>
<h2>子组件 B</h2>
<C-component></C-component>
</div>
</template>
<script>
import CComponent from './C.vue';
export default {
name: 'BComponent',
components: {
CComponent,
},
};
</script>
在 C.vue 中:
<template>
<div>
<h3>子组件 C</h3>
<button @click="passValueToA">向 A 传递值</button>
</div>
</template>
<script>
import { eventBus } from './bus.js';
export default {
name: 'CComponent',
methods: {
passValueToA() {
const valueToPass = 'Hello from C';
// 通过事件总线向父组件 A 传递值
eventBus.$emit('valueFromC', valueToPass);
},
},
};
</script>
在上面的例子中中,我们在 bus.js 中创建了事件总线实例 eventBus。
在组件 A.vue 中,通过 eventBus.$on(‘valueFromC’, this.handleValueFromC) 监听来自 C.vue 的事件,并在 handleValueFromC 方法中处理接收到的数据。
在组件 C.vue 中,当按钮被点击时,通过 eventBus.$emit(‘valueFromC’, valueToPass) 向事件总线发送名为 valueFromC 的事件,并传递值 valueToPass。
这样,通过事件总线,组件 C.vue 可以向组件 A.vue 传递值,而组件 A.vue 可以监听和处理来自 C.vue 的数据。
使用事件总线可以方便地在组件之间传递数据,但在大型应用中可能会导致事件的管理变得困难。
provide/inject
使用 provide 和 inject 是另一种在组件之间传递值的方式。我们在父组件中提供provide数据,然后在子组件中注入inject并使用这些数据。需要注意的是,provide 和 inject 并不是响应式的,这意味着如果提供的值发生变化,子组件不会自动更新。所以基本上很少用到,下面是一个使用 provide 和 inject 进行组件传值的例子:
在父组件 A.vue 中:
<template>
<div>
<h1>父组件 A</h1>
<child-component></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
name: 'AComponent',
components: {
ChildComponent,
},
provide: {
valueFromParent: 'Hello from Parent',
},
};
</script>
在子组件 ChildComponent.vue 中:
<template>
<div>
<h2>子组件</h2>
<p>{{ injectedValue }}</p>
</div>
</template>
<script>
export default {
name: 'ChildComponent',
inject: ['valueFromParent'],
computed: {
injectedValue() {
return this.valueFromParent;
},
},
};
</script>
在上述示例中,父组件 A.vue 使用 provide 选项提供了名为 valueFromParent 的数据,并将其设置为 ‘Hello from Parent’。
在子组件 ChildComponent.vue 中,使用 inject 选项来注入父组件提供的数据。通过在 inject 数组中指定 ‘valueFromParent’,子组件可以访问父组件提供的 valueFromParent 数据。
在子组件中,我们使用 computed 属性来创建一个名为 injectedValue 的计算属性,它返回从父组件注入的值 valueFromParent。
这样,通过使用 provide 和 inject,父组件可以提供数据,而子组件可以通过注入并访问该数据。
再次强调,provide 和 inject 并不是响应式的,这意味着如果提供的值发生变化,子组件不会自动更新。
$attrs/$listeners
a
t
t
r
s
用于传递属性,
attrs 用于传递属性,
attrs用于传递属性,listeners 用于传递事件。下面是一个使用 $attrs 和 $listeners 进行组件传值的例子:
在父组件 A.vue 中:
<template>
<div>
<h1>父组件 A</h1>
<child-component v-bind="$attrs" v-on="$listeners"></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
name: 'AComponent',
components: {
ChildComponent,
},
};
</script>
在子组件 ChildComponent.vue 中:
<template>
<div>
<h2>子组件</h2>
<p>{{ valueFromParent }}</p>
<button @click="$emit('customEvent')">触发自定义事件</button>
</div>
</template>
<script>
export default {
name: 'ChildComponent',
props: {
valueFromParent: String,
},
};
</script>
在上述示例中,父组件 A.vue 使用 v-bind=“$attrs” 将父组件的所有属性传递给子组件。这样,子组件可以接收到来自父组件的属性,并在自身的 props 中声明对应的属性。
父组件 A.vue 同样使用 v-on=“$listeners” 将父组件的所有事件监听器传递给子组件。这样,子组件可以接收到来自父组件的事件,并在自身触发相应的事件。
在子组件 ChildComponent.vue 中,我们声明了一个名为 valueFromParent 的 prop,用于接收来自父组件的值。
子组件中的按钮通过 $emit 方法触发了一个自定义事件 ‘customEvent’。这个事件会被传递给父组件,并在父组件的位置上执行相应的事件处理方法。
通过使用 $attrs 和 $listeners,我们可以将父组件的属性和事件传递给子组件,实现组件之间的值传递和事件触发。
需要注意的是,$attrs 和 $listeners 只会传递父组件中没有被子组件声明为 prop 的属性和事件监听器。$attrs与$listeners 是两个对象,$attrs 里存放的是父组件中绑定的非 Props 属性,$listeners里存放的是父组件中绑定的非原生事件。
总结
这些通信方法各有优劣,适用于不同的场景和需求。例如,Props 和 Emit 是常用的父子组件通信方式,适用于简单的数据传递,且传递是单向和可响应式的。Refs 可以在需要直接访问子组件或 DOM 元素时使用。Event Bus 适用于较为复杂的组件之间的通信。$attrs 和 $listeners 适用于将父组件的属性和事件传递给子组件,而不需要显式声明。Provide 和 Inject 可以在多层级嵌套的组件中传递数据但传递是不可响应式的。
方法 | 总结 |
---|---|
props 和 $emit | 使用 props 属性将数据从父组件传递到子组件。使用 emit 方法触发自定义事件,从子组件向父组件传递数据。 |
$refs | 使用 ref 属性给子组件或 DOM 元素添加引用。 通过 $refs 对象访问子组件或 DOM 元素的实例或属性。 |
Event Bus(事件总线) | 创建一个新的 Vue 实例作为事件总线。使用 $emit 方法触发事件,并使用 $on 方法监听事件,以实现组件之间的通信。 |
Provide 和 Inject | 使用 provide 选项在父组件中提供数据。使用 inject 选项在子组件中注入并使用父组件提供的数据。 |
$attrs 和 $listeners | 使用 v-bind=“ a t t r s " 将父组件的属性传递给子组件。使用 v − o n = " attrs" 将父组件的属性传递给子组件。使用 v-on=" attrs"将父组件的属性传递给子组件。使用v−on="listeners” 将父组件的事件监听器传递给子组件 |
如果有错误,欢迎各位大佬指出!!!