组件通信

组件通信的方式

在 Vue3 中,组件通信有多种方式,每种方式适用于不同的场景。

1. Props 向下传递

Props 是父组件向子组件传递数据的主要方式。

父组件

<template>
  <ChildComponent 
    :message="parentMessage" 
    :user="userInfo" 
    :is-active="true" 
  />
</template>

<script>
export default {
  data() {
    return {
      parentMessage: 'Hello from parent',
      userInfo: {
        name: 'John',
        age: 30
      }
    }
  }
}
</script>

子组件

<template>
  <div>
    <p>{{ message }}</p>
    <p>{{ user.name }} - {{ user.age }}</p>
    <p v-if="isActive">Active</p>
  </div>
</template>

<script>
export default {
  props: {
    // 基本类型
    message: String,
    
    // 对象类型
    user: {
      type: Object,
      required: true
    },
    
    // 带默认值的布尔类型
    isActive: {
      type: Boolean,
      default: false
    }
  }
}
</script>

2. Emits 向上传递事件

子组件通过 emits 向父组件发送事件,父组件可以监听这些事件并执行相应的处理函数。

子组件

<template>
  <button @click="handleClick">点击我</button>
  <input 
    type="text" 
    :value="inputValue" 
    @input="handleInput" 
    placeholder="输入内容"
  />
</template>

<script>
export default {
  name: 'ChildComponent',
  emits: ['click', 'input-change'],
  data() {
    return {
      inputValue: ''
    }
  },
  methods: {
    handleClick() {
      // 发送事件,不带参数
      this.$emit('click')
    },
    handleInput(event) {
      this.inputValue = event.target.value
      // 发送事件,带参数
      this.$emit('input-change', this.inputValue)
    }
  }
}
</script>

父组件

<template>
  <ChildComponent 
    @click="handleChildClick" 
    @input-change="handleInputChange" 
  />
  <p>收到的输入: {{ receivedInput }}</p>
</template>

<script>
export default {
  data() {
    return {
      receivedInput: ''
    }
  },
  methods: {
    handleChildClick() {
      console.log('子组件被点击了')
    },
    handleInputChange(value) {
      this.receivedInput = value
    }
  }
}
</script>

3. Provide / Inject 依赖注入

适用于跨多级组件通信,父组件提供数据,子组件或孙组件注入使用。

父组件(提供数据)

<template>
  <div>
    <ChildComponent />
  </div>
</template>

<script>
export default {
  provide() {
    return {
      appName: 'Vue3 App',
      user: {
        name: 'John',
        age: 30
      },
      updateUser: this.updateUser
    }
  },
  data() {
    return {
      user: {
        name: 'John',
        age: 30
      }
    }
  },
  methods: {
    updateUser(newUser) {
      this.user = newUser
    }
  }
}
</script>

子组件或孙组件(注入数据)

<template>
  <div>
    <p>应用名称: {{ appName }}</p>
    <p>用户: {{ user.name }} - {{ user.age }}</p>
    <button @click="handleUpdate">更新用户</button>
  </div>
</template>

<script>
export default {
  inject: ['appName', 'user', 'updateUser'],
  methods: {
    handleUpdate() {
      this.updateUser({
        name: 'Jane',
        age: 25
      })
    }
  }
}
</script>

4. 组合式 API 中的响应式共享

在组合式 API 中,可以使用响应式 API 结合 provide/inject 实现组件通信。

父组件

<template>
  <div>
    <ChildComponent />
  </div>
</template>

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

const count = ref(0)

function increment() {
  count.value++
}

// 提供响应式数据和方法
provide('count', count)
provide('increment', increment)
</script>

子组件

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

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

// 注入响应式数据和方法
const count = inject('count')
const increment = inject('increment')
</script>

5. Event Bus 事件总线

适用于任意组件间通信,但在 Vue3 中推荐使用 mitt 库实现。

1. 安装 mitt

npm install mitt

2. 创建事件总线

// eventBus.js
import mitt from 'mitt'

export const eventBus = mitt()

3. 发送事件

// ComponentA.vue
import { eventBus } from './eventBus'

eventBus.emit('custom-event', { message: 'Hello from Component A' })

4. 监听事件

// ComponentB.vue
import { eventBus } from './eventBus'
import { onMounted, onUnmounted } from 'vue'

onMounted(() => {
  // 监听事件
  eventBus.on('custom-event', (data) => {
    console.log('收到事件:', data)
  })
})

onUnmounted(() => {
  // 移除事件监听
  eventBus.off('custom-event')
})

6. Vuex / Pinia 状态管理

对于复杂应用,推荐使用 Vuex 或 Pinia 进行集中状态管理。

组件通信方式对比

方式 适用场景 优点 缺点
Props 父到子 简单直接,单向数据流清晰 多级传递繁琐
Emits 子到父 事件驱动,灵活 多级传递繁琐
Provide/Inject 跨多级组件 简化多级组件通信 依赖关系不明确
Event Bus 任意组件 灵活,解耦 事件管理复杂,易内存泄漏
状态管理 复杂应用 集中管理,清晰可预测 增加复杂度,学习成本高

最佳实践

  • 优先使用 Props 和 Emits 进行父子组件通信
  • 跨多级组件通信使用 Provide/Inject
  • 复杂应用使用状态管理库
  • Event Bus 谨慎使用,注意及时清理事件监听
  • 保持组件通信的单向数据流,避免双向绑定导致的复杂度