Props 与自定义事件
在 Vue 3 中,组件之间的通信是构建复杂应用的关键。Props 和自定义事件是实现父子组件通信的基础工具:Props 用于从父组件向子组件传递数据,自定义事件则允许子组件向父组件发送消息。本节将详细讲解这两者的用法和注意事项,并通过示例展示如何在实际项目中应用它们。
Props:父组件向子组件传递数据
Props 是组件的属性,允许父组件通过 HTML 属性向子组件传递数据。子组件通过 props 选项声明接收的数据。
1. 基本用法
示例:传递问候语
创建 src/components/Child.vue:
<template>
<div>
<p>{{ greeting }}, {{ name }}!</p>
</div>
</template>
<script>
export default {
props: {
greeting: {
type: String,
default: 'Hello'
},
name: {
type: String,
required: true
}
},
setup(props) {
return { greeting: props.greeting, name: props.name };
}
};
</script>
在 src/App.vue 中使用:
<template>
<div>
<h1>父组件</h1>
<Child greeting="Hi" name="Alice" />
<Child name="Bob" /> <!-- 使用默认 greeting -->
</div>
</template>
<script>
import Child from './components/Child.vue';
export default {
components: { Child }
};
</script>
- 效果:
父组件 Hi, Alice! Hello, Bob! - 解析:
props定义了greeting(可选,默认值“Hello”)和name(必填)。- 在
setup中通过props参数访问传入值。
2. Props 的验证
Vue 3 支持对 Props 进行类型检查和约束:
type:指定数据类型(如String、Number、Boolean、Array等)。default:设置默认值。required:标记是否必填。validator:自定义验证函数。
示例:带验证的 Props
<template>
<p>年龄:{{ age }}</p>
</template>
<script>
export default {
props: {
age: {
type: Number,
required: true,
validator(value) {
return value >= 0 && value <= 150; // 年龄范围验证
}
}
},
setup(props) {
return { age: props.age };
}
};
</script>
- 效果:若传入的
age不符合验证规则,控制台会警告。
3. 动态 Props
使用 v-bind 动态绑定 Props:
<template>
<div>
<input v-model="inputName" placeholder="输入姓名" />
<Child :name="inputName" />
</div>
</template>
<script>
import { ref } from 'vue';
import Child from './components/Child.vue';
export default {
components: { Child },
setup() {
const inputName = ref('Alice');
return { inputName };
}
};
</script>
- 效果:输入框内容变化时,子组件的
name实时更新。
注意
- Props 是只读的,子组件不应直接修改 Props。若需修改,应通过自定义事件通知父组件。
自定义事件:子组件向父组件通信
自定义事件通过 $emit 方法触发,允许子组件主动向父组件发送数据或信号。
1. 基本用法
示例:计数器组件
创建 src/components/Counter.vue:
<template>
<div>
<p>计数:{{ count }}</p>
<button @click="increment">增加</button>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup(_, { emit }) { // 使用 context 中的 emit
const count = ref(0);
const increment = () => {
count.value++;
emit('count-changed', count.value); // 触发自定义事件
};
return { count, increment };
}
};
</script>
在 src/App.vue 中监听:
<template>
<div>
<h1>父组件计数:{{ parentCount }}</h1>
<Counter @count-changed="updateCount" />
</div>
</template>
<script>
import { ref } from 'vue';
import Counter from './components/Counter.vue';
export default {
components: { Counter },
setup() {
const parentCount = ref(0);
const updateCount = (newCount) => {
parentCount.value = newCount;
};
return { parentCount, updateCount };
}
};
</script>
- 效果:子组件点击按钮时,父组件的
parentCount同步更新。 - 解析:
- 子组件用
emit('count-changed', payload)触发事件。 - 父组件用
@count-changed监听并接收数据。
- 子组件用
2. 事件验证
Vue 3 支持声明自定义事件,增强代码可读性和安全性:
<script>
export default {
emits: ['count-changed'], // 声明事件
setup(_, { emit }) {
const count = ref(0);
const increment = () => {
count.value++;
emit('count-changed', count.value);
};
return { count, increment };
}
};
</script>
- 好处:若触发未声明的事件,Vue 会警告。
带验证的事件
<script>
export default {
emits: {
'count-changed': (value) => {
return typeof value === 'number' && value >= 0; // 验证 payload
}
},
setup(_, { emit }) {
const count = ref(0);
const increment = () => {
count.value++;
emit('count-changed', count.value);
};
return { count, increment };
}
};
</script>
3. v-model 与自定义事件
Vue 3 支持在组件上使用 v-model,底层依赖自定义事件。
示例:自定义输入组件
创建 src/components/MyInput.vue:
<template>
<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>
<script>
export default {
props: {
modelValue: String // v-model 绑定的值
},
emits: ['update:modelValue'] // v-model 约定的事件
};
</script>
在 src/App.vue 中使用:
<template>
<div>
<MyInput v-model="text" />
<p>输入内容:{{ text }}</p>
</div>
</template>
<script>
import { ref } from 'vue';
import MyInput from './components/MyInput.vue';
export default {
components: { MyInput },
setup() {
const text = ref('');
return { text };
}
};
</script>
- 效果:输入内容时,
text双向同步。 - 解析:
v-model自动绑定modelValueProp 和update:modelValue事件。
综合示例
结合 Props 和自定义事件,创建一个任务项组件:
<!-- TaskItem.vue -->
<template>
<div>
<span :class="{ completed: done }">{{ task }}</span>
<button @click="toggleDone">{{ done ? '取消' : '完成' }}</button>
</div>
</template>
<script>
export default {
props: {
task: String,
done: Boolean
},
emits: ['update:done'],
setup(props, { emit }) {
const toggleDone = () => {
emit('update:done', !props.done);
};
return { toggleDone };
}
};
</script>
<style scoped>
.completed {
text-decoration: line-through;
color: gray;
}
</style>
<!-- App.vue -->
<template>
<div>
<h1>任务列表</h1>
<TaskItem v-model:done="task1Done" task="学习 Vue 3" />
<TaskItem v-model:done="task2Done" task="完成项目" />
</div>
</template>
<script>
import { ref } from 'vue';
import TaskItem from './components/TaskItem.vue';
export default {
components: { TaskItem },
setup() {
const task1Done = ref(false);
const task2Done = ref(false);
return { task1Done, task2Done };
}
};
</script>
- 效果:点击按钮切换任务完成状态,样式动态更新。
总结
Props 和自定义事件是 Vue 3 组件通信的基础。Props 实现单向数据流,自定义事件支持子组件主动反馈,二者结合可构建灵活的交互逻辑。掌握这些后,你可以轻松处理父子组件间的协作。下一章将深入响应式系统,进一步提升你的 Vue 3 技能!
