实用指南:Vue2 与 Vue3 父子组件参数传递全解析:从实例到原理

在 Vue 开发中,组件间通信是核心知识点之一,而父子组件间的参数传递更是日常开发中最常遇到的场景。本文将详细对比 Vue2 和 Vue3 中父子组件参数传递的实现方式,通过具体实例分析两者的异同点,并解读背后的设计理念变化。

一、父传子:数据从父组件流向子组件

父组件向子组件传递数据是最基础的组件通信场景,通常用于将初始化数据、配置项等传递给子组件。

Vue2 实现方式

Vue2 中父传子通过props实现,子组件需要在props选项中声明接收的属性:



<script>
import Child from './Child.vue'
export default {
  components: { Child },
  data() {
    return {
      parentMsg: 'Hello from parent',
      user: { name: 'Vue2', age: 6 },
      showChild: true
    }
  }
}
</script>


<script>
export default {
  // 声明接收的props
  props: {
    message: String,
    userInfo: {
      type: Object,
      required: true
    },
    isVisible: {
      type: Boolean,
      default: false
    }
  }
}
</script>

Vue2 的 props 验证支持类型检查、必填项验证、默认值等特性,通过对象形式可以进行更细致的配置。

Vue3 实现方式

Vue3 中父传子同样使用props,但在组合式 API 中通过defineProps宏来声明:



<script setup>
import Child from './Child.vue'
import { ref, reactive } from 'vue'
const parentMsg = ref('Hello from parent')
const user = reactive({ name: 'Vue3', age: 3 })
const showChild = ref(true)
</script>


<script setup>
// 声明接收的props
const props = defineProps({
  message: String,
  userInfo: {
    type: Object,
    required: true
  },
  isVisible: {
    type: Boolean,
    default: false
  }
})
// 在脚本中使用
console.log(props.message)
</script>

Vue3 在script setup中使用defineProps宏,无需导入即可直接使用。此外,Vue3 还支持基于 TypeScript 的 props 类型声明:

// 基于TS的props声明
const props = defineProps<{
  message?: string
  userInfo: {
    name: string
    age: number
  }
  isVisible: boolean
}>()

父传子对比分析

Vue3 的改进主要体现在类型支持和使用便捷性上,defineProps宏配合 TypeScript 可以提供更好的开发体验和类型安全。

二、子传父:数据从子组件流向父组件

子组件向父组件传递数据通常用于将用户操作、组件内部状态变化等通知给父组件。

Vue2 实现方式

Vue2 中子传父通过$emit方法触发自定义事件实现:



<script>
export default {
  data() {
    return {
      inputValue: ''
    }
  },
  methods: {
    handleClick() {
      // 触发自定义事件并传递数据
      this.$emit('button-click', '来自子组件的点击')
    },
    handleInput() {
      this.$emit('input-change', this.inputValue)
    }
  },
  // 可选:声明触发的事件
  emits: ['button-click', 'input-change']
}
</script>


<script>
import Child from './Child.vue'
export default {
  components: { Child },
  methods: {
    handleButtonClick(data) {
      console.log('收到子组件数据:', data)
    },
    handleInputChange(value) {
      console.log('输入框值:', value)
    }
  }
}
</script>

Vue2.2.0 + 新增了emits选项,用于声明组件触发的事件,提高代码可读性和 IDE 支持。

Vue3 实现方式

Vue3 中子传父同样使用事件触发机制,但在组合式 API 中通过defineEmits宏来声明:



<script setup>
import { ref } from 'vue'
const inputValue = ref('')
// 声明触发的事件
const emit = defineEmits(['button-click', 'input-change'])
// 或使用TS类型声明
// const emit = defineEmits<{
//   (e: 'button-click', data: string): void
//   (e: 'input-change', value: string): void
// }>()
const handleClick = () => {
  emit('button-click', '来自子组件的点击')
}
const handleInput = () => {
  emit('input-change', inputValue.value)
}
</script>


<script setup>
import Child from './Child.vue'
const handleButtonClick = (data) => {
  console.log('收到子组件数据:', data)
}
const handleInputChange = (value) => {
  console.log('输入框值:', value)
}
</script>

子传父对比分析

Vue3 的defineEmits提供了更明确的事件类型定义,特别是结合 TypeScript 时,可以获得完整的类型检查和自动补全,减少运行时错误。

三、双向绑定:父子组件数据同步

双向绑定是一种特殊的通信模式,允许父子组件共享同一状态并保持同步。

Vue2 实现方式

Vue2 中通过.sync修饰符或v-model实现双向绑定:





this.$emit('update:visible', false)




export default {
  props: ['value'],
  methods: {
    updateValue(newVal) {
      this.$emit('input', newVal)
    }
  }
}

Vue2 中一个组件只能有一个v-model,如果需要多个双向绑定属性,需使用.sync修饰符。

Vue3 实现方式

Vue3 对双向绑定进行了统一和增强,使用v-model配合参数的方式:



<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const isVisible = ref(true)
const msg = ref('Hello')
</script>


<script setup>
import { ref, toRef } from 'vue'
const props = defineProps(['visible', 'message'])
const emit = defineEmits(['update:visible', 'update:message'])
const localMsg = toRef(props, 'message')
const close = () => {
  emit('update:visible', false)
}
const updateMsg = () => {
  emit('update:message', localMsg.value)
}
</script>

Vue3 中v-model可以接受一个参数,从而实现多属性的双向绑定,取代了 Vue2 中的.sync修饰符,使 API 更加统一。

四、父子组件访问:直接操作实例

在某些特殊场景下,可能需要直接访问父子组件的实例或 DOM 元素。

Vue2 实现方式

Vue2 中通过$parent$children访问父子组件实例,通过ref获取 DOM 或组件引用:



<script>
export default {
  mounted() {
    // 访问子组件方法
    this.$refs.childRef.childMethod()
    // 访问父组件
    console.log(this.$parent)
  }
}
</script>

<script>
export default {
  mounted() {
    // 访问父组件数据
    console.log(this.$parent.parentData)
  }
}
</script>

Vue3 实现方式

Vue3 中取消了$children,推荐使用ref配合defineExpose来暴露子组件的属性和方法:



<script setup>
import { ref, onMounted } from 'vue'
import Child from './Child.vue'
const childRef = ref(null)
onMounted(() => {
  // 访问子组件暴露的方法
  childRef.value.childMethod()
})
</script>

<script setup>
import { ref } from 'vue'
const count = ref(0)
const childMethod = () => {
  count.value++
}
// 显式暴露属性和方法
defineExpose({
  childMethod,
  count
})
</script>

五、总结与最佳实践

对比 Vue2 和 Vue3 的父子组件通信方式,可以发现以下变化趋势:

  1. API 简化:Vue3 通过definePropsdefineEmits宏简化了代码,减少了模板代码量
  2. 类型强化:Vue3 对 TypeScript 的原生支持使组件通信更加类型安全
  3. 明确性提升defineExpose要求显式暴露组件 API,使组件接口更加清晰
  4. 双向绑定统一:Vue3 的v-model参数语法统一了双向绑定的实现方式

最佳实践建议:

  1. 优先使用propsemit进行父子通信,保持单向数据流
  2. 避免过度使用$parent或直接访问组件实例,降低耦合度
  3. 复杂场景下可以考虑使用provide/inject或状态管理库
  4. 使用 TypeScript 提升类型安全性和开发体验
  5. 组件通信应遵循 "单一职责" 原则,每个组件只关注自身功能

Vue3 在保持 Vue2 核心思想的基础上,通过 API 的优化和类型系统的增强,使父子组件通信更加直观、安全和高效。理解这些变化不仅有助于我们更好地使用 Vue3,也能加深对组件化思想的理解。

posted @ 2025-11-19 13:12  yangykaifa  阅读(0)  评论(0)    收藏  举报