1. 父组件向子组件传值:props
props
是父组件向子组件传递数据的主要方式。
示例代码
<!-- 父组件 Parent.vue -->
<template>
<div>
<Child :message=parentMessage />
</div>
</template>
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const parentMessage = ref('Hello from parent');
</script>
<!-- 子组件 Child.vue -->
<template>
<div>
{{ message }}
</div>
</template>
<script setup>
const props = defineProps({
message: String
});
</script>
解析
- 父组件:在父组件中,通过在子组件标签上使用
:
(即v-bind
的缩写)绑定一个数据属性,将parentMessage
传递给子组件。 - 子组件:在子组件中,使用
defineProps
宏来定义接收的props
。defineProps
接收一个对象,对象的键是props
的名称,值是该props
的类型。子组件可以在模板中直接使用接收到的props
。
2. 子组件向父组件传值:emits
和自定义事件
子组件通过触发自定义事件向父组件传递数据。
示例代码
<!-- 父组件 Parent.vue -->
<template>
<div>
<Child @childEvent=handleChildEvent />
<p>Received from child: {{ receivedMessage }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const receivedMessage = ref('');
const handleChildEvent = (message) => {
receivedMessage.value = message;
};
</script>
<!-- 子组件 Child.vue -->
<template>
<button @click=sendMessage>Send message to parent</button>
</template>
<script setup>
import { defineEmits } from 'vue';
const emits = defineEmits(['childEvent']);
const sendMessage = () => {
emits('childEvent', 'Hello from child');
};
</script>
解析
- 子组件:使用
defineEmits
宏定义可以触发的自定义事件。在子组件的方法中,通过emits
函数触发自定义事件,并传递数据。 - 父组件:在父组件中,使用
@
(即v-on
的缩写)监听子组件的自定义事件,并定义一个方法来处理该事件。当子组件触发事件时,父组件的处理方法会被调用,并接收子组件传递的数据。
3. 兄弟组件传值:事件总线或状态管理库
事件总线
事件总线是一个简单的全局事件系统,用于组件之间的通信。
示例代码
// eventBus.js
import { ref, reactive } from 'vue';
const eventBus = reactive({
events: {},
on(event, callback) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(callback);
},
emit(event, data) {
if (this.events[event]) {
this.events[event].forEach((callback) => callback(data));
}
},
off(event, callback) {
if (this.events[event]) {
const index = this.events[event].indexOf(callback);
if (index !== -1) {
this.events[event].splice(index, 1);
}
}
},
});
export default eventBus;
<!-- 组件 A.vue -->
<template>
<button @click=sendMessage>Send message to sibling</button>
</template>
<script setup>
import eventBus from './eventBus.js';
const sendMessage = () => {
eventBus.emit('siblingEvent', 'Hello from sibling A');
};
</script>
<!-- 组件 B.vue -->
<template>
<p>Received from sibling: {{ receivedMessage }}</p>
</template>
<script setup>
import { ref } from 'vue';
import eventBus from './eventBus.js';
const receivedMessage = ref('');
const handleSiblingEvent = (message) => {
receivedMessage.value = message;
};
eventBus.on('siblingEvent', handleSiblingEvent);
</script>
解析
- 事件总线:创建一个全局的事件总线对象,包含
on
、emit
和off
方法,分别用于监听事件、触发事件和取消监听事件。 - 组件通信:一个组件通过
emit
方法触发事件并传递数据,另一个组件通过on
方法监听该事件,并在回调函数中处理接收到的数据。
状态管理库(以 Pinia 为例)
Pinia 是 Vue 3 推荐的状态管理库,用于在多个组件之间共享状态。
示例代码
// stores/counter.js
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++;
}
}
});
<!-- 组件 A.vue -->
<template>
<button @click=increment>Increment</button>
</template>
<script setup>
import { useCounterStore } from '../stores/counter';
const counterStore = useCounterStore();
const increment = () => {
counterStore.increment();
};
</script>
<!-- 组件 B.vue -->
<template>
<p>Count: {{ counterStore.count }}</p>
</template>
<script setup>
import { useCounterStore } from '../stores/counter';
const counterStore = useCounterStore();
</script>
解析
- 状态管理:使用
defineStore
定义一个 store,包含状态和操作状态的方法。 - 组件使用:多个组件可以通过
useCounterStore
函数获取同一个 store 实例,从而共享状态和调用操作状态的方法。
4. 跨层级组件传值:provide
和 inject
provide
和 inject
用于在跨层级的组件之间传递数据。
示例代码
<!-- 祖先组件 Ancestor.vue -->
<template>
<div>
<Descendant />
</div>
</template>
<script setup>
import { provide, ref } from 'vue';
import Descendant from './Descendant.vue';
const sharedData = ref('Shared data from ancestor');
provide('sharedData', sharedData);
</script>
<!-- 后代组件 Descendant.vue -->
<template>
<div>
{{ injectedData }}
</div>
</template>
<script setup>
import { inject } from 'vue';
const injectedData = inject('sharedData');
</script>
解析
- 祖先组件:使用
provide
函数提供一个数据,第一个参数是提供的数据的键,第二个参数是要提供的数据。 - 后代组件:使用
inject
函数注入祖先组件提供的数据,参数是要注入的数据的键。后代组件可以直接使用注入的数据。