前言
本系列将从零开始,系统性地介绍 Vue 3 的常用 API,逐步深入每个核心概念与功能模块。通过详尽的讲解与实战演示,帮助大家掌握 Vue 3 的基础与进阶知识,最终具备独立搭建完整 Vue 3 项目的能力。
vue3中的组件通信
Vue 3 提供了多种方式来进行组件之间的通信。根据场景的不同,开发者可以选择最合适的方式进行数据的传递与事件的处理。
1. 通过 Props 传递数据(父 -> 子)
父组件:
<template>
<ChildComponent :message="parentMessage" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
const parentMessage = 'Hello from Parent';
</script>
子组件:
<template>
//写法1
<p>{{ message }}</p>
//写法2
<p>{{ props.message }}</p>
</template>
<script setup>
//写法1
defineProps({
message: String
});
//写法2
const props=defineProps({
message: String
});
</script>
2.通过 Emit 传递事件(子 -> 父)
子组件可以通过 emit 函数向父组件传递事件。不过写法和vue2有点不一样。
子组件:
<template>
<button @click="sendMessage">Send Message</button>
</template>
<script setup>
//创建一个emit,
//defineEmits数组内包含的是传给父组件的方法,多个的话以逗号隔开,
// 例子: defineEmits(['message','xxx']);
const emit = defineEmits(['message']);
const sendMessage = () => {
//使用emit像父组件传值,message为方法名,逗号后面为传递的值
//如果是定义的值直接写入就行,这里例子使用的是一个字符串
emit('message', 'Hello from Child');
}
</script>
父组件:
<template>
//和vue2一样的写法
<ChildComponent @message="handleMessage" />
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
const handleMessage = (msg) => {
console.log(msg);
}
</script>
3.兄弟组件通信 (Provide & Inject) :用的较少
通过 provide 和 inject,我们可以在兄弟组件之间实现数据共享。
在父先组件中使用 provide: 提供数据或方法。
父组件:
<template>
<div>
<ChildComponentA />
<ChildComponentB />
</div>
</template>
<script setup>
import { ref, provide } from 'vue';
import ChildComponentA from './ChildComponentA.vue';
import ChildComponentB from './ChildComponentB.vue';
const sharedData = ref('Hello from Parent');
provide('sharedData', sharedData);
function updateData(newData) {
sharedData.value = newData;
}
provide('updateData', updateData);
</script>
</script>
子组件A:
<template>
<div>
<p>Data: {{ sharedData }}</p>
<button @click="changeData">Change Data</button>
</div>
</template>
<script setup>
import { inject } from 'vue';
const sharedData = inject('sharedData');
const updateData = inject('updateData');
function changeData() {
updateData('Data changed by Component A');
}
</script>
子组件B:
<template>
<div>
<p>Data: {{ sharedData }}</p>
</div>
</template>
<script setup>
import { inject } from 'vue';
const sharedData = inject('sharedData');
</script>
原理:
- 父组件使用 provide 提供了一个响应式数据 sharedData 和一个方法 updateData。这些可以被子组件访问
- 子组件A使用 inject 获取 sharedData 和 updateData,并通过按钮点击更新数据。
- 子组件B使用 inject 获取 sharedData,当数据被兄弟组件 A 更新时,它会自动响应并更新显示。
简单的数据传递可以考虑这种方式,如果是复杂的还是选择Pinia吧,后面会有一篇专门讲解Pinia的文章,感兴趣的小伙伴可以去点评一下哦~
4.全局状态管理 (Pinia)
在复杂场景中,使用 Pinia 或 Vuex 可以更高效地进行全局状态管理。
定义 store:
import { defineStore } from 'pinia';
export const useStore = defineStore('main', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++;
}
}
});
在组件中使用 Pinia:
<template>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</template>
<script setup>
import { useStore } from './store';
const store = useStore();
const { count, increment } = store;
</script>
5. ref 和父子组件的事件绑定
ref 可以用来获取子组件实例或 DOM 节点,并直接调用子组件方法。用法和vue2差不多,只是写法有一些变化
父组件:
<template>
<ChildComponent ref="childRef" />
<button @click="callChildMethod">Call Child Method</button>
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const childRef = ref(null);
const callChildMethod = () => {
childRef.value.someMethod();
}
</script>
子组件:
<script setup>
const someMethod = () => {
console.log('Child method called');
}
</script>
6.自定义事件(通过 v-model 进行双向绑定)
自定义事件和 v-model 结合使用可以实现组件的双向绑定。这个功能允许你在父组件和子组件之间同步数据。
父组件:
<template>
<div>
<h1>Parent Component</h1>
<ChildComponent v-model="parentData" />
<p>Parent Data: {{ parentData }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const parentData = ref('Initial data');
</script>
子组件:
<template>
<div>
<input :value="modelValue" @input="onInputChange" />
</div>
</template>
<script setup>
import { defineEmits, defineProps } from 'vue';
// 定义接收的 props
const props = defineProps({
modelValue: String
});
// 定义 emit 事件
const emit = defineEmits(['update:modelValue']);
const onInputChange = (event) => {
emit('update:modelValue', event.target.value);
};
</script>
原理:
- 子组件: 子组件通过 props 接收数据,通过 emit 触发更新事件。v-model 默认使用 modelValue 和
update:modelValue 作为 prop 和事件名称。 - 父组件: 父组件通过 v-model 进行绑定,当子组件触发 update:modelValue 事件时,父组件的对应数据会自动更新。
自定义属性名
可以自定义 v-model 的属性名。例如,如果想用 value 和 input 代替 modelValue 和 update:modelValue:
子组件:
<template>
<div>
<input :value="value" @input="onInputChange" />
</div>
</template>
<script setup>
import { defineEmits, defineProps } from 'vue';
// 定义接收的 props
const props = defineProps({
value: String
});
// 定义 emit 事件
const emit = defineEmits(['input']);
const onInputChange = (event) => {
emit('input', event.target.value);
};
</script>
父组件:
<template>
<div>
<h1>Parent Component</h1>
<ChildComponent v-model:value="parentData" />
<p>Parent Data: {{ parentData }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const parentData = ref('Initial data');
</script>
总结
在 Vue 3 中,组件通信是构建动态、交互式应用的核心。了解并掌握不同的组件通信方式能帮助我们更高效地管理数据流动和事件处理。
- Props 传递数据(父 -> 子): 使用 props 在父组件向子组件传递数据是最基础的通信方式。
- Emit 传递事件(子 -> 父): 子组件使用 emit 向父组件传递事件,以实现事件驱动的通信。
- Provide & Inject(兄弟组件间通信): 通过 provide 和 inject 可以在组件树中实现数据共享,适合简单的状态共享。
- 全局状态管理 (Pinia): 在复杂场景中,使用 Pinia(或 Vuex)进行全局状态管理,提供了一种高效的状态共享和管理方式。
- ref 和父子组件的事件绑定: 使用 ref 获取子组件实例,允许父组件直接调用子组件的方法。
- 自定义事件和 v-model 双向绑定: 结合自定义事件与 v-model 实现数据的双向绑定,使父子组件间的数据同步更为简便。
每种通信方式都有其适用场景,选择合适的方式能够提高应用的可维护性和可扩展性。在实际开发中,根据应用需求灵活运用这些方式,能有效提升开发效率和代码质量。