Vue3 组件化开发核心:组件定义、Props 与 Emits 父子传值

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 的注意事项

  1. 单向数据流:Props 是只读的,子组件不应直接修改。
  2. 动态与静态传递 :
    • 静态:<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 组件拆分原则

  1. 单一职责:每个组件只负责一个特定功能
  2. 高内聚低耦合:组件内部紧密相关,组件之间依赖明确
  3. 可复用性:设计通用的、可配置的组件
  4. 清晰的接口: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 让逻辑组织更加灵活。掌握这些核心概念后,可以构建出可维护、可复用的前端应用。建议在实际项目中多加练习,不断优化组件设计。

相关推荐
Amos_Web6 分钟前
Rspack 源码解析(三十一):HotModuleReplacementPlugin 如何用 hash 比对生成增量更新
前端
尤水就下17 分钟前
🔥说一说在线文档的 Block 架构是如何实现的
前端·javascript·架构
是江迪呀1 小时前
介绍一个开源项目 —— 烂笔头
前端·后端
恋猫de小郭1 小时前
Flutter 紧急发布 3.47.8 安全升级版本,建议都升级
android·前端·flutter
紧跟先前的步伐1 小时前
Go AI Agent 实战 18|Tool Calling 的基本原理与完整执行流程
前端·数据库·人工智能·ai·golang·1024程序员节
captain3761 小时前
Spring Web MVC(2)
java·前端·spring boot·后端·spring·mvc
晓得迷路了1 小时前
栗子前端技术周刊第 151 期 - ViteConf 2026、React Router 8.5、React Native 0.88...
前端·javascript·vue.js
ai工具集合1 小时前
老项目位图图标放大模糊?PNG 一键转 SVG 矢量化实操,附前端避坑指南
前端
2601_968708031 小时前
DreamsChat Web – Chat, Audio, Video Web APP with Admin Dashboard
前端·架构
Ai-_Man1 小时前
Microsoft Copilot对话数据怎么导入到另外一个Microsoft Copilot账号里去?AI导出鸭
开发语言·javascript·人工智能·microsoft·chatgpt·ecmascript·copilot