在 Vue的开发过程中,父子组件之间的通信是极为常见且重要的操作,其中传值以及方法调用是实现组件间交互的关键环节。Vue2 和 Vue3 在这方面既有相似之处,又存在一些显著差异。本文将通过具体代码呈现 Vue2 与 Vue3 父子组件传值及方法调用的差异。
1. vue3中的父子组件交互
1.1. 父组件向子组件传值
Vue3 引入了 defineProps
宏。defineProps
有两种使用方式,数组形式和对象形式,与 Vue2 的 props
接收方式类似,但更加简洁。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <!-- 父组件 --> <template> <div> <ChildComponent :message="parentMessage" :count="parentCount" /> </div> </template>
<script setup> import { ref } from 'vue';
const parentMessage = ref('Hello from parent'); const parentCount = ref(10); </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <!-- 子组件 --> <template> <div> <p>{{ message }}</p> <p>{{ count }}</p> </div> </template>
<script setup> // 数组形式接收 const props = defineProps(['message', 'count']);
// 对象形式接收(示例注释) // const props = defineProps({ // message: { // type: String, // required: true // }, // count: { // type: Number, // default: 0 // } // }); </script>
|
1.2. 父组件调用子组件方法
Vue3 中父组件调用子组件的方法同样使用 ref
,但在 <script setup>
中需要先定义 ref
变量来引用子组件。与 Vue2 不同的是,子组件需要通过 defineExpose
来显式暴露要被父组件访问的方法和属性,否则父组件无法直接访问。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <!-- 父组件 --> <template> <div> <ChildComponent ref="childRef" /> <button @click="callChildMethod">Call Child Method</button> </div> </template>
<script setup> import { ref } from 'vue';
const childRef = ref(null);
const callChildMethod = () => { if (childRef.value) { childRef.value.childMethod(); } }; </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <!-- 子组件 --> <template> <div> <p>Child Component</p> </div> </template>
<script setup> import { defineExpose } from 'vue';
const childMethod = () => { console.log('Child method called'); };
defineExpose({ childMethod }); </script>
|
1.3. 子组件调用父组件方法并传值
子组件向父组件传值同样是通过触发自定义事件,在 Vue3 中可以使用 defineEmits
宏来声明要触发的自定义事件。子组件通过 emit
函数触发事件并传递数据,父组件监听子组件标签上的自定义事件来接收数据。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <!-- 子组件 --> <template> <button @click="sendDataToParent">Send Data</button> </template>
<script setup> const emit = defineEmits(['childEvent']);
const sendDataToParent = () => { const data = 'Hello from child'; emit('childEvent', data); }; </script>
|
1 2 3 4 5 6 7 8 9 10 11 12
| <!-- 父组件 --> <template> <div> <ChildComponent @childEvent="handleChildEvent" /> </div> </template>
<script setup> const handleChildEvent = (data) => { console.log('Received from child:', data); }; </script>
|
2. vue2中的父子组件交互
2.1. 父组件向子组件传值
在 Vue2 中,父组件向子组件传值主要通过 props
属性。父组件在引用子组件时,通过在子组件标签上定义自定义属性来传递数据。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <!-- 父组件 --> <template> <div> <ChildComponent :message="parentMessage" :count="parentCount" /> </div> </template>
<script> export default { data() { return { parentMessage: 'Hello from parent', parentCount: 10 }; } }; </script>
|
子组件则通过 props
选项来接收父组件传递的数据,接收方式有数组形式和对象形式。数组形式较为简单,直接列出要接收的属性名;对象形式可以对接收的数据进行类型校验、设置默认值等操作。例如:
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
| <!-- 子组件 --> <template> <div> <p>{{ message }}</p> <p>{{ count }}</p> </div> </template>
<script> export default { // 数组形式接收 props: ['message', 'count'], // 对象形式接收(示例注释) // props: { // message: { // type: String, // required: true // }, // count: { // type: Number, // default: 0 // } // } }; </script>
|
2.2. 父组件调用子组件方法
在 Vue2 中,父组件调用子组件的方法主要通过 $refs
。父组件在引用子组件时,给子组件添加 ref
属性,然后在需要调用子组件方法时,通过 this.$refs
获取子组件实例,进而调用其方法。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <!-- 父组件 --> <template> <div> <ChildComponent ref="childRef" /> <button @click="callChildMethod">Call Child Method</button> </div> </template>
<script> export default { methods: { callChildMethod() { this.$refs.childRef.childMethod(); } } }; </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <!-- 子组件 --> <template> <div> <p>Child Component</p> </div> </template>
<script> export default { methods: { childMethod() { console.log('Child method called'); } } }; </script>
|
2.3. 子组件向父组件传值
子组件向父组件传值则是通过 $emit
方法触发自定义事件来实现。子组件在需要向父组件传递数据时,调用 $emit
并传入自定义事件名和要传递的数据。父组件在引用子组件时,通过在子组件标签上监听该自定义事件来接收数据。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <!-- 子组件 --> <template> <button @click="sendDataToParent">Send Data</button> </template>
<script> export default { methods: { sendDataToParent() { const data = 'Hello from child'; this.$emit('childEvent', data); } } }; </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <!-- 父组件 --> <template> <div> <ChildComponent @childEvent="handleChildEvent" /> </div> </template>
<script> export default { methods: { handleChildEvent(data) { console.log('Received from child:', data); } } }; </script>
|
2.4. 子组件调用父组件方法
子组件访问父组件的方法既可以通过$emit
的方式,也可以通过 this.$parent
,直接访问父组件的实例,进而调用其方法。但这种方式在多层嵌套组件中可能会导致依赖混乱,不推荐过度使用。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <!-- 子组件 --> <template> <button @click="callParentMethod">Call Parent Method</button> </template>
<script> export default { methods: { callParentMethod() { this.$parent.parentMethod(); } } }; </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <!-- 父组件 --> <template> <div> <ChildComponent /> </div> </template>
<script> export default { methods: { parentMethod() { console.log('Parent method called'); } } }; </script>
|
3. 差异对比
语法差异:
Vue2 使用选项式 API,代码结构基于 data
、methods
、props
等选项。
Vue3 推荐使用组合式 API,特别是 <script setup>
语法糖,使得代码更加简洁、逻辑更加集中。例如在接收父组件传值时,Vue2 需要在 props
选项中定义,而 Vue3 通过 defineProps
在 <script setup>
中直接定义,减少了样板代码。
方法调用的访问权限差异:
Vue2 中子组件的所有方法和属性默认对父组件可见,父组件可以直接通过 $refs
访问子组件的任何方法和属性。
Vue3 中,子组件需要通过 defineExpose
显式暴露方法和属性,父组件才能访问,这增强了组件的封装性和安全性,避免了不必要的属性和方法暴露。