Vue 3 组件通信教程

Vue 3 组件通信教程

1. Props 父传子

1.1 基础用法

在 Vue 3 中,我们使用 defineProps 来声明组件的 props:

vue 复制代码
<!-- 子组件 ChildComponent.vue -->
<script setup>
const props = defineProps({
  message: String,
  count: {
    type: Number,
    required: true,
    default: 0
  },
  items: {
    type: Array,
    default: () => []
  }
})
</script>

<template>
  <div>
    <p>{{ message }}</p>
    <p>Count: {{ count }}</p>
  </div>
</template>

父组件中使用:

vue 复制代码
<!-- 父组件 ParentComponent.vue -->
<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

const parentMessage = ref('Hello from parent')
const parentCount = ref(42)
</script>

<template>
  <ChildComponent 
    :message="parentMessage"
    :count="parentCount"
  />
</template>

1.2 Props 验证

Props 可以设置详细的验证规则:

vue 复制代码
<script setup>
const props = defineProps({
  // 基础类型检查
  propA: Number,
  // 多种类型
  propB: [String, Number],
  // 必填字段
  propC: {
    type: String,
    required: true
  },
  // 带有默认值
  propD: {
    type: Number,
    default: 100
  },
  // 带有默认值的对象
  propE: {
    type: Object,
    default: () => ({ message: 'hello' })
  },
  // 自定义验证函数
  propF: {
    validator(value) {
      return ['success', 'warning', 'danger'].includes(value)
    }
  }
})
</script>

2. Emits 子传父

2.1 基础用法

使用 defineEmits 声明事件:

vue 复制代码
<!-- 子组件 ChildComponent.vue -->
<script setup>
const emit = defineEmits(['update', 'delete'])

const handleClick = () => {
  emit('update', { id: 1, data: 'new value' })
}
</script>

<template>
  <button @click="handleClick">更新数据</button>
</template>

父组件中接收事件:

vue 复制代码
<!-- 父组件 ParentComponent.vue -->
<script setup>
import ChildComponent from './ChildComponent.vue'

const handleUpdate = (payload) => {
  console.log('收到更新:', payload)
}
</script>

<template>
  <ChildComponent @update="handleUpdate" />
</template>

2.2 带验证的 Emits

vue 复制代码
<script setup>
const emit = defineEmits({
  // 不带验证函数
  click: null,
  // 带验证函数
  submit: (payload) => {
    if (!payload.email) {
      return false
    }
    return true
  }
})
</script>

3. v-model 双向绑定

3.1 基础用法

vue 复制代码
<!-- 子组件 CustomInput.vue -->
<script setup>
defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>

<template>
  <input
    :value="modelValue"
    @input="emit('update:modelValue', $event.target.value)"
  />
</template>

父组件中使用:

vue 复制代码
<script setup>
import { ref } from 'vue'
import CustomInput from './CustomInput.vue'

const text = ref('')
</script>

<template>
  <CustomInput v-model="text" />
  <p>输入的内容:{{ text }}</p>
</template>

3.2 多个 v-model 绑定

vue 复制代码
<!-- 子组件 UserForm.vue -->
<script setup>
defineProps(['firstName', 'lastName'])
const emit = defineEmits(['update:firstName', 'update:lastName'])
</script>

<template>
  <input
    :value="firstName"
    @input="emit('update:firstName', $event.target.value)"
  />
  <input
    :value="lastName"
    @input="emit('update:lastName', $event.target.value)"
  />
</template>

父组件使用:

vue 复制代码
<script setup>
import { ref } from 'vue'
import UserForm from './UserForm.vue'

const firstName = ref('')
const lastName = ref('')
</script>

<template>
  <UserForm
    v-model:firstName="firstName"
    v-model:lastName="lastName"
  />
</template>

4. provide/inject 依赖注入

4.1 基础用法

vue 复制代码
<!-- 父组件提供数据 -->
<script setup>
import { provide, ref } from 'vue'

const theme = ref('dark')
provide('theme', theme)
</script>

<!-- 子组件注入数据 -->
<script setup>
import { inject } from 'vue'

const theme = inject('theme', 'light') // 第二个参数是默认值
</script>

4.2 响应式数据注入

vue 复制代码
<!-- 父组件 -->
<script setup>
import { provide, ref, readonly } from 'vue'

const count = ref(0)
const incrementCount = () => {
  count.value++
}

// 提供只读值和修改方法
provide('count', readonly(count))
provide('increment', incrementCount)
</script>

<!-- 子组件 -->
<script setup>
import { inject } from 'vue'

const count = inject('count')
const increment = inject('increment')
</script>

<template>
  <button @click="increment">{{ count }}</button>
</template>

5. EventBus 事件总线

虽然 Vue 3 移除了内置的事件总线,但我们可以使用第三方库或自己实现一个简单的事件总线:

javascript 复制代码
// eventBus.js
import mitt from 'mitt'
export default mitt()

使用示例:

vue 复制代码
<!-- 组件 A -->
<script setup>
import eventBus from './eventBus'

const sendMessage = () => {
  eventBus.emit('custom-event', { message: 'Hello!' })
}
</script>

<!-- 组件 B -->
<script setup>
import { onMounted, onUnmounted } from 'vue'
import eventBus from './eventBus'

const handleEvent = (data) => {
  console.log(data.message)
}

onMounted(() => {
  eventBus.on('custom-event', handleEvent)
})

onUnmounted(() => {
  eventBus.off('custom-event', handleEvent)
})
</script>

6. refs 直接访问

6.1 模板引用

vue 复制代码
<!-- 父组件 -->
<script setup>
import { ref, onMounted } from 'vue'
import ChildComponent from './ChildComponent.vue'

const childRef = ref(null)

onMounted(() => {
  // 访问子组件的方法或属性
  childRef.value.someMethod()
})
</script>

<template>
  <ChildComponent ref="childRef" />
</template>

<!-- 子组件 -->
<script setup>
// 需要显式暴露方法给父组件
defineExpose({
  someMethod() {
    console.log('方法被调用')
  }
})
</script>

最佳实践建议

  1. 优先使用 props 和 emits 进行父子组件通信
  2. 对于跨多层组件的通信,考虑使用 provide/inject
  3. 对于全局状态管理,使用 Vuex 或 Pinia
  4. 避免过度使用 EventBus,它可能导致维护困难
  5. 谨慎使用 refs 直接访问子组件,这可能破坏组件封装性

注意事项

  1. Props 是只读的,不要在子组件中直接修改
  2. 使用 v-model 时注意命名冲突
  3. provide/inject 的响应式数据建议使用 readonly 包装
  4. 在组件卸载时记得清理事件监听器
  5. 使用 TypeScript 时,建议为 props 和 emits 添加类型声明
相关推荐
狗哥哥26 分钟前
Vue 3 动态菜单渲染优化实战:从白屏到“零延迟”体验
前端·vue.js
青青很轻_28 分钟前
Vue自定义拖拽指令架构解析:从零到一实现元素自由拖拽
前端·javascript·vue.js
蜗牛攻城狮39 分钟前
Vue 中 `scoped` 样式的实现原理详解
前端·javascript·vue.js
q_19132846951 小时前
基于SpringBoot2+Vue2的行业知识答题考试系统
java·vue.js·spring boot·mysql·毕业设计·计算机毕业设计·演示文稿
方安乐2 小时前
vue3 el-select懒加载以及自定义指令
javascript·vue.js·elementui
老华带你飞3 小时前
二手商城|基于springboot 二手商城系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring
老华带你飞3 小时前
酒店预约|基于springboot 酒店预约系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring
代码续发4 小时前
自定义指令
javascript·vue.js·ecmascript
Irene19914 小时前
Vue 3 项目创建方式对比(npm create vue@latest -- --typescript --eslint --prettier 自带格式化)
vue.js
用户841794814565 小时前
vxe-table 实现滚动加载数据,无限加载数据教程
vue.js