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

相关推荐
天蓝色的鱼鱼1 分钟前
让 AI 写代码半年后,我发现了几个藏在项目里的安全隐患
前端·javascript·ai编程
随风一样自由17 分钟前
【前端+跨端技术新格局】Tauri vs Electron 完整性能对比(2026实测数据+底层原理)
前端·javascript·electron
L-影23 分钟前
Pydantic 模型:为你的 Web 数据装上“安检系统”
前端
Reload.36 分钟前
GJ航司,验证码网易易盾 JS逆向 纯算轨迹
开发语言·前端·javascript
L-影38 分钟前
FastAPI 请求头与 Cookie:Web 世界的“身份证”与“通行证”
前端·fastapi
IT_陈寒39 分钟前
Vue的响应式更新把我坑惨了,原来是这个原因
前端·人工智能·后端
铁皮饭盒44 分钟前
Bun.js 集成了 SQL/Redis/S3,为什么 Java/Go/Python 不集成?
javascript
年轻的砖头1 小时前
以Ajax方式提交整个表单
前端·javascript·ajax
L-影1 小时前
FastAPI CORS 跨域:Web 世界的“小区门禁”与“访客通行证”
前端·javascript·fastapi
stereohomology1 小时前
已进入初赛提交Demo的两个项目:(2)单独html钢琴
前端·html·why不coding