第3章:组件化开发基础

第8节:自定义事件与组件通信

3.8.1 自定义事件定义与触发

在Vue.js中,子组件可以通过自定义事件向父组件传递数据和通知。子组件使用$emit()方法触发事件,父组件通过v-on@指令监听事件。

子组件触发自定义事件

<!-- 子组件 ChildComponent.vue -->
<template>
  <button @click="$emit('enlarge-text', 0.1)">
    放大文字
  </button>
</template>

在选项式API中,也可以在方法中触发事件:

<!-- 子组件 ChildComponent.vue -->
<template>
  <button @click="handleClick">
    放大文字
  </button>
</template>

<script>
export default {
  methods: {
    handleClick() {
      // 触发自定义事件,并传递参数
      this.$emit('enlarge-text', 0.1)
    }
  }
}
</script>

父组件监听自定义事件

<!-- 父组件 ParentComponent.vue -->
<template>
  <div>
    <p :style="{ fontSize: postFontSize + 'em' }">这是一段文字</p>
    <ChildComponent @enlarge-text="postFontSize += $event" />
  </div>
</template>

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

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      postFontSize: 1
    }
  }
}
</script>

在组合式API中使用自定义事件

<!-- 子组件 ChildComponent.vue -->
<template>
  <button @click="emit('enlarge-text', 0.1)">
    放大文字
  </button>
</template>

<script setup>
// 定义组件可以触发的事件
const emit = defineEmits(['enlarge-text'])
</script>

3.8.2 事件验证与.emits选项

在Vue 3中,可以使用.emits选项对自定义事件进行验证和类型检查。

事件验证(选项式API)

export default {
  // 声明组件可以触发的事件
  emits: {
    // 简单声明
    'enlarge-text': null,
    // 带验证的事件
    'update:modelValue': (value) => {
      // 验证函数,返回true表示验证通过
      return typeof value === 'number' || typeof value === 'string'
    }
  },
  methods: {
    handleClick() {
      this.$emit('enlarge-text', 0.1)
    }
  }
}

在组合式API中验证事件

<script setup>
// 带验证的事件声明
const emit = defineEmits({
  'enlarge-text': (value) => {
    return typeof value === 'number' && value > 0
  }
})

// 使用TypeScript类型
const emit = defineEmits<{
  (e: 'enlarge-text', value: number): void
  (e: 'close'): void
}>()
</script>

3.8.3 v-model在组件上的使用

v-model是Vue的双向绑定指令,它可以在组件上使用,实现父子组件之间的数据双向同步。

基本用法

<!-- 父组件 -->
<template>
  <CustomInput v-model="searchText" />
</template>

<!-- 等价于 -->
<template>
  <CustomInput
    :modelValue="searchText"
    @update:modelValue="newValue => searchText = newValue"
  />
</template>

子组件实现

<!-- 子组件 CustomInput.vue -->
<template>
  <input
    :value="modelValue"
    @input="$emit('update:modelValue', ($event.target as HTMLInputElement).value)"
  />
</template>

<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>

使用自定义prop和事件名

可以通过v-model的参数来自定义prop和事件名:

<!-- 父组件 -->
<template>
  <CustomInput v-model:search="searchText" />
</template>

<!-- 子组件 CustomInput.vue -->
<template>
  <input
    :value="search"
    @input="$emit('update:search', ($event.target as HTMLInputElement).value)"
  />
</template>

<script setup>
const props = defineProps(['search'])
const emit = defineEmits(['update:search'])
</script>

3.8.4 多个v-model绑定

在Vue 3中,一个组件可以支持多个v-model绑定:

<!-- 父组件 -->
<template>
  <UserName
    v-model:first-name="firstName"
    v-model:last-name="lastName"
  />
</template>

<!-- 子组件 UserName.vue -->
<template>
  <div>
    <input
      type="text"
      :value="firstName"
      @input="$emit('update:first-name', ($event.target as HTMLInputElement).value)"
    />
    <input
      type="text"
      :value="lastName"
      @input="$emit('update:last-name', ($event.target as HTMLInputElement).value)"
    />
  </div>
</template>

<script setup>
const props = defineProps(['firstName', 'lastName'])
const emit = defineEmits(['update:first-name', 'update:last-name'])
</script>

自定义事件最佳实践

  1. 使用语义化的事件名:事件名应该清晰反映事件的含义,如updateclosesubmit
  2. 遵循kebab-case命名:事件名使用kebab-case(短横线分隔),如enlarge-text
  3. 明确事件参数:为事件传递必要的参数,方便父组件处理
  4. 使用.emits选项:声明组件可以触发的事件,提高代码的可维护性
  5. 避免过度使用事件:对于复杂的组件通信,可以考虑使用状态管理库

总结

自定义事件是Vue.js中实现子组件向父组件通信的重要方式。通过掌握自定义事件的定义、触发、监听和验证,以及v-model在组件上的使用,你可以实现灵活高效的组件间通信。

在下一节中,我们将学习组件间通信的另一种方式:插槽(Slots)系统。

« 上一篇 Props组件通信 下一篇 » 插槽(Slots)系统