Vue2 与 Vue3 自定义事件实现对比

Vue2通过$emit触发事件,父组件使用@监听;


Vue3引入defineEmits()和emits选项显式声明事件,支持TypeScript类型检查。


主要差异:Vue3支持多v-model、移除.sync修饰符、提供事件验证,组合式API更灵活。


迁移时需注意移除$listeners、native修饰符,改用v-model:propName替代.sync。


Vue3事件系统更明确、类型安全,推荐使用defineEmits()声明事件。


Vue2 与 Vue3 自定义事件实现对比

一、Vue2 中的自定义事件

1. 子组件触发事件

vue

javascript 复制代码
<template>
  <button @click="emitCustomEvent">触发事件</button>
</template>

<script>
export default {
  methods: {
    emitCustomEvent() {
      // 触发自定义事件
      this.$emit('custom-event', {
        message: '来自子组件的数据',
        timestamp: Date.now()
      });
      
      // 触发带修饰符的事件
      this.$emit('update:title', '新标题');
    }
  }
}
</script>

2. 父组件监听事件

vue

javascript 复制代码
<template>
  <div>
    <child-component 
      @custom-event="handleCustomEvent"
      @update:title="title = $event"
    />
    <p>收到消息: {{ message }}</p>
    <p>标题: {{ title }}</p>
  </div>
</template>

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

export default {
  components: { ChildComponent },
  data() {
    return {
      message: '',
      title: '原始标题'
    };
  },
  methods: {
    handleCustomEvent(payload) {
      this.message = payload.message;
      console.log('收到事件:', payload);
    }
  }
}
</script>

3. 使用 v-model 语法糖

vue

javascript 复制代码
<!-- 子组件 -->
<script>
export default {
  model: {
    prop: 'value',
    event: 'change'
  },
  props: ['value'],
  methods: {
    updateValue(newValue) {
      this.$emit('change', newValue);
    }
  }
}
</script>

<!-- 父组件 -->
<child-component v-model="dataValue" />

二、Vue3 中的自定义事件

1. Composition API 方式

vue

javascript 复制代码
<template>
  <button @click="emitEvent">触发事件</button>
</template>

<script setup>
import { defineEmits } from 'vue';

// 定义可触发的事件
const emit = defineEmits([
  'custom-event',
  'update:title',
  'change'  // 用于 v-model
]);

const emitEvent = () => {
  // 触发事件
  emit('custom-event', {
    message: '来自子组件的数据',
    timestamp: Date.now()
  });
  
  // 触发带修饰符的事件
  emit('update:title', '新标题');
};
</script>

<!-- 或者使用类型声明 -->
<script setup lang="ts">
const emit = defineEmits<{
  (e: 'custom-event', payload: { message: string, timestamp: number }): void
  (e: 'update:title', value: string): void
  (e: 'change', value: any): void
}>();
</script>

2. Options API 方式

vue

javascript 复制代码
<template>
  <button @click="emitEvent">触发事件</button>
</template>

<script>
export default {
  emits: ['custom-event', 'update:title', 'change'], // 显式声明事件
  
  // 或带验证
  emits: {
    'custom-event': (payload) => {
      return payload && typeof payload.message === 'string';
    }
  },
  
  methods: {
    emitEvent() {
      this.$emit('custom-event', {
        message: '数据',
        timestamp: Date.now()
      });
    }
  }
}
</script>

3. 父组件监听事件

vue

javascript 复制代码
<template>
  <div>
    <ChildComponent 
      @custom-event="handleCustomEvent"
      @update:title="title = $event"
      v-model:title="title"  <!-- Vue3 支持多个 v-model -->
    />
    <p>收到消息: {{ message }}</p>
    <p>标题: {{ title }}</p>
  </div>
</template>

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

const message = ref('');
const title = ref('原始标题');

const handleCustomEvent = (payload) => {
  message.value = payload.message;
  console.log('收到事件:', payload);
};
</script>

三、主要差异对比

特性 Vue2 Vue3
事件定义 不需要显式声明 emits 选项或 defineEmits()
语法糖 单个 v-model 多个 v-model 绑定
修饰符 .sync 修饰符 内置 v-model 支持参数
组合式API 不支持 defineEmits()
类型支持 有限 完整的 TypeScript 支持
事件验证 无内置验证 支持事件参数验证

四、最佳实践示例

Vue3 完整示例

vue

javascript 复制代码
<!-- CustomButton.vue -->
<template>
  <button @click="handleClick">
    <slot>点击我</slot>
  </button>
</template>

<script setup lang="ts">
interface EmitEvents {
  (e: 'click', event: MouseEvent): void
  (e: 'custom-click', payload: { id: number, value: string }): void
  (e: 'update:modelValue', value: boolean): void
}

const emit = defineEmits<EmitEvents>();

const handleClick = (event: MouseEvent) => {
  // 触发原生事件
  emit('click', event);
  
  // 触发自定义事件
  emit('custom-click', {
    id: 1,
    value: '按钮被点击'
  });
  
  // 触发 v-model 更新
  emit('update:modelValue', true);
};
</script>

<!-- 使用组件 -->
<template>
  <CustomButton 
    v-model="isActive"
    @custom-click="handleCustomClick"
    @click="handleNativeClick"
  />
</template>

事件验证示例

vue

javascript 复制代码
<script>
export default {
  emits: {
    // 验证提交事件
    submit: (payload) => {
      // 必须返回布尔值表示验证是否通过
      return (
        payload &&
        typeof payload.email === 'string' &&
        payload.email.includes('@') &&
        typeof payload.password === 'string' &&
        payload.password.length >= 6
      );
    }
  },
  
  methods: {
    handleSubmit() {
      const payload = {
        email: this.email,
        password: this.password
      };
      
      // 验证失败会在控制台警告
      this.$emit('submit', payload);
    }
  }
}
</script>

五、迁移注意事项

  1. .sync 修饰符 :Vue3 中已移除,使用 v-model:propName 替代

  2. $listeners :Vue3 中已移除,监听器直接作为 $attrs 的一部分

  3. 事件名大小写:Vue3 中推荐使用 kebab-case,但 camelCase 也能工作

  4. native 修饰符 :Vue3 中已移除,所有事件都通过 emits 定义


Vue3 的事件系统更加明确和类型安全,推荐总是使用 emits 选项或 defineEmits() 来声明组件可以触发的事件。

相关推荐
zhengxianyi5153 小时前
ruoyi-vue-pro优化——如何将一个模块快速变成一个独立的应用进行开发,部署,管理
vue.js·前后端分离·数据大屏·ruoyi-vue-pro优化
zhengxianyi5155 小时前
ruoyi-vue-pro优化——让菜单支持多个参数,一键直达【经营分析】、【生产报表】、【销售报表】
vue.js·前后端分离·数据大屏·ruoyi-vue-pro优化
bug总结5 小时前
身份证号脱敏的正确实现
前端·javascript·vue.js
xkxnq6 小时前
第二阶段:Vue 组件化开发(第 19天)
前端·javascript·vue.js
虚诚7 小时前
vue2中树形表格怎么实现
前端·javascript·vue.js·ecmascript·vue2·树形结构
CUYG8 小时前
v-model封装组件(定义 model 属性)
前端·vue.js
五仁火烧10 小时前
npm run build命令详解
前端·vue.js·npm·node.js
志摩凛10 小时前
Element UI 长表单校验失败后自动展开折叠面板并滚动定位
数据结构·vue.js
xiaoyan201510 小时前
2026原创Electron39.2+Vue3+DeepSeek从0-1手搓AI模板桌面应用Exe
vue.js·electron·deepseek