Skip to content

组件通信

组件通信是Vue3中一个重要的概念,它允许不同组件之间传递数据和事件。本章节将详细介绍Vue3中的组件通信方式,包括props、emit、provide/inject等。

组件通信的方式

在Vue3中,组件通信主要有以下几种方式:

  1. 父传子:使用props
  2. 子传父:使用emit
  3. 祖孙通信:使用provide/inject
  4. 全局通信:使用状态管理(如Pinia)

父传子:props

1. 基本用法

父组件可以通过props向子组件传递数据:

vue
<!-- ParentComponent.vue -->
<template>
  <div>
    <ChildComponent title="Hello Vue3!" message="Welcome to Vue3 component" />
  </div>
</template>

<script setup>
import ChildComponent from './ChildComponent.vue'
</script>

<!-- ChildComponent.vue -->
<template>
  <div class="child">
    <h1>{{ title }}</h1>
    <p>{{ message }}</p>
  </div>
</template>

<script setup>
defineProps({
  title: String,
  message: String
})
</script>

2. Props验证

你可以为props添加类型验证和默认值:

vue
<template>
  <div class="child">
    <h1>{{ title }}</h1>
    <p>{{ message }}</p>
    <p>Count: {{ count }}</p>
  </div>
</template>

<script setup>
defineProps({
  title: {
    type: String,
    required: true
  },
  message: {
    type: String,
    default: 'Default message'
  },
  count: {
    type: Number,
    default: 0
  }
})
</script>

3. 传递复杂数据

你可以传递对象、数组等复杂数据:

vue
<!-- ParentComponent.vue -->
<template>
  <div>
    <ChildComponent :user="user" :items="items" />
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'
import ChildComponent from './ChildComponent.vue'

const user = reactive({
  name: 'John',
  age: 25
})

const items = ref([
  { id: 1, name: 'Item 1' },
  { id: 2, name: 'Item 2' },
  { id: 3, name: 'Item 3' }
])
</script>

<!-- ChildComponent.vue -->
<template>
  <div class="child">
    <p>Name: {{ user.name }}</p>
    <p>Age: {{ user.age }}</p>
    <ul>
      <li v-for="item in items" :key="item.id">
        {{ item.name }}
      </li>
    </ul>
  </div>
</template>

<script setup>
defineProps({
  user: Object,
  items: Array
})
</script>

子传父:emit

1. 基本用法

子组件可以通过emit向父组件传递事件:

vue
<!-- ParentComponent.vue -->
<template>
  <div>
    <p>Count: {{ count }}</p>
    <ChildComponent @increment="handleIncrement" @decrement="handleDecrement" />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

const count = ref(0)

const handleIncrement = () => {
  count.value++
}

const handleDecrement = () => {
  count.value--
}
</script>

<!-- ChildComponent.vue -->
<template>
  <div class="child">
    <button @click="emit('increment')">Increment</button>
    <button @click="emit('decrement')">Decrement</button>
  </div>
</template>

<script setup>
const emit = defineEmits(['increment', 'decrement'])
</script>

2. 传递参数

你可以在emit事件中传递参数:

vue
<!-- ParentComponent.vue -->
<template>
  <div>
    <p>Message: {{ message }}</p>
    <ChildComponent @update-message="handleUpdateMessage" />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

const message = ref('Hello')

const handleUpdateMessage = (newMessage) => {
  message.value = newMessage
}
</script>

<!-- ChildComponent.vue -->
<template>
  <div class="child">
    <input v-model="inputValue" type="text">
    <button @click="emit('update-message', inputValue)">Update Message</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const inputValue = ref('')
const emit = defineEmits(['update-message'])
</script>

3. 事件验证

在Vue3中,你可以为emit事件添加验证:

vue
<template>
  <div class="child">
    <input v-model="inputValue" type="text">
    <button @click="emit('update-message', inputValue)">Update Message</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const inputValue = ref('')
const emit = defineEmits({
  'update-message': (value) => {
    if (value.length < 3) {
      console.error('Message must be at least 3 characters long')
      return false
    }
    return true
  }
})
</script>

祖孙通信:provide/inject

1. 基本用法

provide/inject允许祖先组件向后代组件传递数据,无论组件层级有多深:

vue
<!-- GrandparentComponent.vue -->
<template>
  <div class="grandparent">
    <h1>Grandparent</h1>
    <ParentComponent />
  </div>
</template>

<script setup>
import { ref, provide } from 'vue'
import ParentComponent from './ParentComponent.vue'

const message = ref('Hello from Grandparent')

// 提供数据
provide('message', message)
</script>

<!-- ParentComponent.vue -->
<template>
  <div class="parent">
    <h2>Parent</h2>
    <ChildComponent />
  </div>
</template>

<script setup>
import ChildComponent from './ChildComponent.vue'
</script>

<!-- ChildComponent.vue -->
<template>
  <div class="child">
    <h3>Child</h3>
    <p>{{ message }}</p>
  </div>
</template>

<script setup>
import { inject } from 'vue'

// 注入数据
const message = inject('message')
</script>

2. 提供默认值

你可以为inject提供默认值:

vue
<template>
  <div class="child">
    <h3>Child</h3>
    <p>{{ message }}</p>
  </div>
</template>

<script setup>
import { inject } from 'vue'

// 注入数据,提供默认值
const message = inject('message', 'Default message')
</script>

3. 传递响应式数据

provide/inject可以传递响应式数据:

vue
<!-- GrandparentComponent.vue -->
<template>
  <div class="grandparent">
    <h1>Grandparent</h1>
    <button @click="message.value = 'Updated message'">Update Message</button>
    <ParentComponent />
  </div>
</template>

<script setup>
import { ref, provide } from 'vue'
import ParentComponent from './ParentComponent.vue'

const message = ref('Hello from Grandparent')

// 提供响应式数据
provide('message', message)
</script>

<!-- ChildComponent.vue -->
<template>
  <div class="child">
    <h3>Child</h3>
    <p>{{ message.value }}</p>
  </div>
</template>

<script setup>
import { inject } from 'vue'

// 注入响应式数据
const message = inject('message')
</script>

全局通信:状态管理

对于复杂的应用,你可以使用状态管理库(如Pinia)来实现全局通信:

1. 安装Pinia

bash
npm install pinia

2. 创建Store

javascript
// src/stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0
  }),
  getters: {
    doubleCount: (state) => state.count * 2
  },
  actions: {
    increment() {
      this.count++
    },
    decrement() {
      this.count--
    }
  }
})

3. 在组件中使用

vue
<!-- ComponentA.vue -->
<template>
  <div class="component-a">
    <h2>Component A</h2>
    <p>Count: {{ counter.count }}</p>
    <button @click="counter.increment">Increment</button>
  </div>
</template>

<script setup>
import { useCounterStore } from '../stores/counter'

const counter = useCounterStore()
</script>

<!-- ComponentB.vue -->
<template>
  <div class="component-b">
    <h2>Component B</h2>
    <p>Count: {{ counter.count }}</p>
    <p>Double Count: {{ counter.doubleCount }}</p>
    <button @click="counter.decrement">Decrement</button>
  </div>
</template>

<script setup>
import { useCounterStore } from '../stores/counter'

const counter = useCounterStore()
</script>

组件通信的最佳实践

1. 选择合适的通信方式

  • 父传子:使用props,适合简单的父子组件通信
  • 子传父:使用emit,适合子组件向父组件传递事件
  • 祖孙通信:使用provide/inject,适合深层级组件通信
  • 全局通信:使用状态管理,适合复杂应用的全局状态管理

2. 保持通信清晰

  • 为props和emit事件使用清晰、语义化的命名
  • 避免过度使用provide/inject,以免使组件之间的依赖关系变得模糊
  • 对于复杂的应用,合理使用状态管理

3. 性能考虑

  • 对于频繁变化的数据,使用响应式数据
  • 对于大型应用,考虑使用状态管理库
  • 避免在props中传递过多数据,只传递必要的数据

总结

组件通信是Vue3中一个重要的概念,它允许不同组件之间传递数据和事件。通过props、emit、provide/inject和状态管理,你可以实现各种场景下的组件通信。

在选择组件通信方式时,你应该根据组件之间的关系和数据的复杂度来选择合适的方式,保持通信清晰,同时考虑性能因素。

在后续的章节中,我们将学习Vue3的组件插槽,包括默认插槽、具名插槽和作用域插槽等内容。

© 2026 编程马·菜鸟教程 版权所有