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 让逻辑组织更加灵活。掌握这些核心概念后,可以构建出可维护、可复用的前端应用。建议在实际项目中多加练习,不断优化组件设计。

相关推荐
郭老二14 小时前
【前端】vue3:编译步骤
前端
不在逃避q15 小时前
Trae/Vs Code/Cursor命令行无法跑npm命令
前端·arcgis·npm
夏殇之殁15 小时前
包中创建自定义列表项。 . 使用自定义列表项进行数据绑定 . 将天气预报数据保存到本地内存表,通过LiveBindings进行显示。 ...
服务器·前端·javascript
The Chosen One98515 小时前
高进度算法模板速记(待完善)
java·前端·算法
陈随易16 小时前
MoonBit抓包模块pcap,查看电脑的每一次联网通信
前端·后端·程序员
新中地GIS开发老师17 小时前
WebGIS开发学生作品|低空航天管理与航线规划系统
前端·javascript·webgis·三维gis开发
用户0595401744617 小时前
大模型对话记忆持久化踩坑实录:用 Playwright 自动化测了 300 次,终于揪出会话丢失的真凶
前端·css
丙氨酸長鏈17 小时前
Web前端入门第 问:JavaScript 一个简单的 IndexedDB 数据库入门示例
前端·javascript·数据库
kyriewen18 小时前
面试官让我手写虚拟列表——AI生成的版本,快速滚动几下就白屏了
前端·javascript·面试
IT_陈寒18 小时前
Vite热更新失效?你可能漏了这个配置
前端·人工智能·后端