Bootstrap

详细分析Vue3中的defineEmits基本知识(子传父)

前言

对于Vue3中的基本知识推荐阅读:

  1. 详细分析Vue3中组合式API(附Demo)
  2. 详细分析Vue3中的props用法(父传子)
  3. 详细分析Vue3中的emit用法(子传父)

1. 基本知识

defineEmits 是 Vue 3 的组合式 API(Composition API)的一部分,用于在组件中定义和使用自定义事件

它替代了 Vue 2 中的 this.$emitthis.$on,让事件的定义和使用变得更加清晰和类型安全,尤其在 TypeScript 中

defineEmits 的基本用法

  1. 定义事件:在组件内部通过 defineEmits 定义事件
    可以传递一个数组或对象来定义事件名和事件参数

  2. 触发事件:使用 emit 方法触发定义的事件

  3. 接收事件:在父组件中使用自定义事件监听器来接收事件

2. Demo

2.1 传值

基本的用法如下:

在子组件中,定义两个事件:increment 和 decrement。当按钮被点击时,相应的事件会被触发

<template>
  <div>
    <button @click="handleIncrement">Increment</button>
    <button @click="handleDecrement">Decrement</button>
  </div>
</template>

<script setup lang="ts">
const emit = defineEmits(['increment', 'decrement']);

const handleIncrement = () => {
  emit('increment');
};

const handleDecrement = () => {
  emit('decrement');
};
</script>

在父组件中,监听子组件触发的 increment 和 decrement 事件,并通过相应的处理函数来更新计数器

<template>
  <div>
    <p>Counter: {{ counter }}</p>
    <ChildComponent @increment="increment" @decrement="decrement" />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const counter = ref(0);

const increment = () => {
  counter.value++;
};

const decrement = () => {
  counter.value--;
};
</script>

2.2 传参

子组件中,定义一个事件 updateValue,它会传递一个数值参数

<template>
  <div>
    <button @click="update(1)">Increment</button>
    <button @click="update(-1)">Decrement</button>
  </div>
</template>

<script setup lang="ts">
const emit = defineEmits<{ (e: 'updateValue', value: number): void }>();

const update = (value: number) => {
  emit('updateValue', value);
};
</script>

在父组件中,接收 updateValue 事件,并使用传递的参数更新计数器

<template>
  <div>
    <p>Counter: {{ counter }}</p>
    <ChildComponent @updateValue="updateCounter" />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const counter = ref(0);

const updateCounter = (value: number) => {
  counter.value += value;
};
</script>

2.3 传对象

在对象中,键是事件名,值是一个函数,该函数的参数就是事件的参数

子组件 使用对象语法定义事件和参数类型

<template>
  <div>
    <button @click="update(1)">Increment</button>
    <button @click="update(-1)">Decrement</button>
  </div>
</template>

<script setup lang="ts">
const emit = defineEmits({
  updateValue: (value: number) => true,
});

const update = (value: number) => {
  emit('updateValue', value);
};
</script>

在父组件中,接收 updateValue 事件,并使用传递的参数更新计数器

<template>
  <div>
    <p>Counter: {{ counter }}</p>
    <ChildComponent @updateValue="updateCounter" />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const counter = ref(0);

const updateCounter = (value: number) => {
  counter.value += value;
};
</script>

3. 实战

先给一个实战的例子:

整体逻辑如下:

  1. 定义和触发事件
    使用 defineEmits([‘success’]) 定义了一个 success 事件
    在表单提交成功后,通过 emit(‘success’) 触发 success 事件

  2. 表单提交逻辑
    submitForm 函数负责表单提交逻辑
    首先校验表单数据
    根据表单类型 (create 或 update) 发起相应的 API 请求
    请求成功后,弹出成功消息并关闭表单对话框
    最后触发 success 事件,通知父组件刷新列表

  3. 父组件监听事件
    父组件 EnterpriseRegistry 监听子组件 EnterpriseRegistryForm 的 success 事件
    在监听到 success 事件后,调用 getList 方法重新获取列表数据

子组件(表单组件)

// 定义和触发 success 事件
const emit = defineEmits(['success']);

const submitForm = async () => {
  await formRef.value.validate();
  formLoading.value = true;
  try {
    const data = formData.value as unknown as EnterpriseRegistryVO;
    if (formType.value === 'create') {
      await EnterpriseRegistryApi.createEnterpriseRegistry(data);
      message.success(t('common.createSuccess'));
    } else {
      await EnterpriseRegistryApi.updateEnterpriseRegistry(data);
      message.success(t('common.updateSuccess'));
    }
    dialogVisible.value = false;
    emit('success'); // 触发 success 事件
  } finally {
    formLoading.value = false;
  }
};

父组件:

<EnterpriseRegistryForm ref="formRef" @success="getList" />

对应的方法:

const getList = async () => {
  loading.value = true;
  try {
    const data = await EnterpriseRegistryApi.getEnterpriseRegistryPage(queryParams);
    list.value = data.list;
    total.value = data.total;
  } finally {
    loading.value = false;
  }
};

对应的逻辑如下:

  1. defineEmits 定义事件:在子组件中使用 defineEmits 定义事件,事件名可以是字符串数组或对象形式,用于明确事件类型和参数
  2. emit 触发事件:通过调用 emit 方法触发已定义的事件,并可携带相关参数
  3. 父组件监听事件:父组件通过 @事件名 监听子组件触发的事件,从而在事件触发时执行相应的处理逻辑
;