1. Vue3 组件定义与 Props 父子传值
1.1 Props:父组件向子组件传递数据
Props 是父组件向子组件传递数据的单向数据流。
父组件传递数据
vue
<!-- ParentComponent.vue -->
<template>
<div>
<h2>父组件</h2>
<!-- 使用 v-bind 或 : 传递数据 -->
<ChildComponent :message="parentMessage" />
</div>
</template>
<script setup>
import ChildComponent from './ChildComponent.vue'
import { ref } from 'vue'
const parentMessage = ref('这是来自父组件的问候!')
</script>
Props 的类型与验证
可以为 Props 指定类型并进行验证,提高代码健壮性。
javascript
const props = defineProps({
// 基础类型检查
title: String,
// 多个可能的类型
value: [String, Number],
// 必填项
userId: {
type: Number,
required: true
},
// 带默认值
size: {
type: String,
default: 'medium'
},
// 自定义验证函数
score: {
type: Number,
validator: (value) => {
return value >= 0 && value <= 100
}
}
})
1.2 使用 Props 的注意事项
- 单向数据流:Props 是只读的,子组件不应直接修改。
- 动态与静态传递 :
- 静态:
<Child title="静态标题" /> - 动态:
<Child :title="dynamicTitle" />
- 静态:
2. 掌握 defineEmits 子向父传值
2.1 使用 defineEmits 定义自定义事件
子组件通过触发事件向父组件通信。
vue
<!-- ChildComponent.vue -->
<template>
<div>
<h3>子组件</h3>
<button @click="sendMessage">向父组件发送消息</button>
</div>
</template>
<script setup>
import { defineEmits } from 'vue'
// 定义可触发的事件
const emit = defineEmits(['message-sent', 'update:count'])
const sendMessage = () => {
// 触发事件并传递数据
emit('message-sent', '你好,父组件!')
// 触发更新事件(常用于 v-model 双向绑定)
emit('update:count', 10)
}
</script>
2.2 父组件监听子组件事件
vue
<!-- ParentComponent.vue -->
<template>
<div>
<h2>父组件</h2>
<p>收到子组件消息:{{ receivedMessage }}</p>
<p>当前计数:{{ count }}</p>
<!-- 监听子组件事件 -->
<ChildComponent
@message-sent="handleMessage"
@update:count="count = $event"
/>
</div>
</template>
<script setup>
import ChildComponent from './ChildComponent.vue'
import { ref } from 'vue'
const receivedMessage = ref('')
const count = ref(0)
const handleMessage = (msg) => {
receivedMessage.value = msg
console.log('收到子组件消息:', msg)
}
</script>
2.3 事件验证与类型提示
可以为事件添加验证和类型提示(TypeScript)。
typescript
// 类型提示
const emit = defineEmits<{
(e: 'message-sent', message: string): void
(e: 'update:count', value: number): void
}>()
// 带验证的事件定义
const emit = defineEmits({
'message-sent': (payload: string) => {
// 返回 true 表示验证通过
return payload.length > 0
}
})
3. 学习组件生命周期(onMounted 等)
3.1 Vue3 生命周期钩子
在组合式 API 中,生命周期钩子以 onXxx 函数形式导入使用。
vue
<script setup>
import { onMounted, onUpdated, onUnmounted, onBeforeMount } from 'vue'
// 组件挂载后执行
onMounted(() => {
console.log('组件已挂载到 DOM')
// 可以在这里进行 DOM 操作、发起网络请求等
})
// 组件更新后执行
onUpdated(() => {
console.log('组件已更新')
})
// 组件卸载前执行
onUnmounted(() => {
console.log('组件即将卸载')
// 清理定时器、取消事件监听等
})
// 组件挂载前执行
onBeforeMount(() => {
console.log('组件即将挂载')
})
</script>
3.2 完整生命周期流程图
是
否
是
否
开始
setup
onBeforeMount
onMounted
数据更新?
onBeforeUpdate
onUpdated
组件卸载?
onBeforeUnmount
onUnmounted
结束
3.3 常见使用场景
| 生命周期钩子 | 使用场景 |
|---|---|
| onMounted | DOM 操作、数据初始化、事件监听、API 请求 |
| onUpdated | DOM 更新后的操作、依赖更新的计算 |
| onUnmounted | 清理定时器、取消事件监听、释放资源 |
| onBeforeMount | 服务器端渲染、最后时刻的状态修改 |
4. 练习简单组件拆分与复用
4.1 组件拆分原则
- 单一职责:每个组件只负责一个特定功能
- 高内聚低耦合:组件内部紧密相关,组件之间依赖明确
- 可复用性:设计通用的、可配置的组件
- 清晰的接口:Props 和 Emits 定义明确
4.2 实战:按钮组件
vue
<!-- ReusableButton.vue -->
<template>
<button
:class="['btn', `btn-${type}`, { 'btn-disabled': disabled }]"
:disabled="disabled"
@click="handleClick"
>
<slot>{{ text }}</slot>
</button>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
type: {
type: String,
default: 'primary',
validator: (value) => ['primary', 'secondary', 'danger'].includes(value)
},
text: {
type: String,
default: '按钮'
},
disabled: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['click'])
const handleClick = () => {
if (!props.disabled) {
emit('click')
}
}
</script>
<style scoped>
.btn {
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.btn-primary {
background-color: #007bff;
color: white;
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-danger {
background-color: #dc3545;
color: white;
}
.btn-disabled {
opacity: 0.6;
cursor: not-allowed;
}
</style>
4.3 使用示例
vue
<!-- App.vue -->
<template>
<div>
<h1>按钮组件示例</h1>
<ReusableButton
type="primary"
text="主要按钮"
@click="handlePrimaryClick"
/>
<ReusableButton
type="secondary"
@click="handleSecondaryClick"
>
自定义内容
</ReusableButton>
<ReusableButton
type="danger"
text="危险按钮"
:disabled="true"
/>
</div>
</template>
<script setup>
import ReusableButton from './components/ReusableButton.vue'
const handlePrimaryClick = () => {
console.log('主要按钮被点击')
}
const handleSecondaryClick = () => {
console.log('次要按钮被点击')
}
</script>
总结:Vue3 的组件化开发通过清晰的 Props 和 Emits 接口实现了父子组件通信,组合式 API 让逻辑组织更加灵活。掌握这些核心概念后,可以构建出可维护、可复用的前端应用。建议在实际项目中多加练习,不断优化组件设计。