模式切换
Props 和自定义事件(父子通信)
在 Vue 中,父子组件通信主要通过 Props(父传子)和自定义事件(子传父)实现。
Props(父传子)
作用
- 父组件通过属性向子组件传递数据。
基本用法
- 父组件传递数据html
<ChildComponent :title="parentTitle" :count="parentCount" />
javascriptimport { ref } from 'vue'; const parentTitle = ref('我是父组件标题'); const parentCount = ref(0);
- 子组件接收数据vue
<script setup> // 组合式 API:defineProps 宏 const props = defineProps({ title: String, // 类型校验 count: { type: Number, default: 1, // 默认值 required: true // 必传 } }); </script> <template> <h2>{{ title }}</h2> <p>计数:{{ count }}</p> </template>
关键特性
- 类型校验
- 支持
String
、Number
、Boolean
、Array
、Object
等原生类型,或自定义类。
javascriptdefineProps({ user: Object, tags: Array, callback: Function });
- 支持
- 单向数据流
- Props 是只读的,子组件不能直接修改父组件传递的值。
- 如需修改,父组件通过事件接收子组件的请求,由父组件更新数据。
- 动态 Props
- 使用
v-bind
动态绑定
html<ChildComponent :config="dynamicConfig" />
- 使用
自定义事件(子传父)
作用
- 子组件通过触发事件向父组件传递数据或通知状态变化。
基本用法
- 子组件触发事件vue
<script setup> // 组合式 API:defineEmits 宏 const emit = defineEmits(['updateTitle', 'deleteItem']); const handleClick = () => { emit('updateTitle', '新标题'); // 触发事件并传递数据 }; </script> <template> <button @click="handleClick">修改标题</button> </template>
- 父组件监听事件html
<ChildComponent @update-title="handleUpdateTitle" @delete-item="handleDelete" />
javascriptconst handleUpdateTitle = (newTitle) => { console.log('收到新标题:', newTitle); parentTitle.value = newTitle; // 更新父组件数据 };
关键特性
- 事件命名规范
- 推荐使用 kebab-case(如
update-title
),因为 HTML 属性不区分大小写。
- 推荐使用 kebab-case(如
- 传递多个参数javascript
emit('event-name', arg1, arg2, ...);
- 事件校验
- 通过对象语法校验事件参数:
javascriptconst emit = defineEmits({ 'update-title': (newTitle) => { return typeof newTitle === 'string'; // 返回布尔值表示校验是否通过 } });
高级技巧
- 双向绑定简化(v-model)
- 副组件通过
v-model
实现双向绑定(语法糖):
html<!-- 父组件 --> <ChildComponent v-model="parentData" /> <!-- 等价于 --> <ChildComponent :modelValue="parentData" @update:modelValue="parentData = $event" />
- 子组件需定义
modelValue
和触发update:modelValue
:
javascriptdefineProps(['modelValue']); const emit = defineEmits(['update:modelValue']);
- 副组件通过
- 透传 Attributes(
$attrs
)- 父组件传递未在子组件
props
中声明的属性,会透传到子组件的根元素。 - 禁用透传:
inheritAttrs: false
。
- 父组件传递未在子组件
- 依赖注入(
provide/inject
)- 跨层级组件通信(避免逐层传递
props
):
javascript// 祖先组件 provide('theme', 'dark'); // 后代组件 const theme = inject('theme', 'light'); // 默认值 'light'
- 跨层级组件通信(避免逐层传递
对比总结
通信方式 | 方向 | 适用场景 |
---|---|---|
Props | 父 → 子 | 父组件向子组件传递初始数据 |
自定义事件 | 子 → 父 | 子组件通知父组件状态变化 |
v-model | 双向绑定 | 表单控件或自定义组件简化双向绑定 |
provide/inject | 跨层级 | 深层嵌套组件共享数据 |
常见问题
Q1:为什么不能直接修改 Props?
- 单向数据流原则:保证数据源唯一性,避免父/子组件数据不一致。
- 正确做法:子组件触发事件,由父组件修改数据。
Q2:如何监听子组件的生命周期?
- 父组件使用
@hook:
或@vnode-
事件(Vue 3 已废弃,改用emits
)。
Q3:动态 Props 名如何实现?
- 使用
v-bind:[propName]
:
html
<ChildComponent :[dynamicPropName]="value"/>