$emit与作用域插槽核心对比及示例代码

$emit与slot作用域插槽核心对比及示例代码

$emit 和作用域插槽的核心对比 + 完整可运行的示例代码,用最清晰的方式帮你区分这两种传值方式,一眼看懂它们的差异和适用场景。

一、核心对比表

维度 this.$emit(子传父事件) 作用域插槽(Scoped Slot)
数据流方向 子组件 → 父组件(事件+数据) 子组件 → 父组件(仅暴露数据)
核心目的 子组件触发事件,通知父组件处理逻辑 子组件提供数据,父组件自定义渲染内容
使用方式 子组件 $emit('事件名', 数据),父组件 @事件名="方法" 子组件 <slot :数据名="数据">,父组件 v-slot="{数据名}"
耦合度 子组件需提前定义事件,耦合度较高 子组件只提供数据,不关心渲染,耦合度极低
灵活性 父组件只能处理逻辑,无法自定义子组件内容 父组件可完全自定义子组件内的渲染内容
适用场景 表单提交、弹窗关闭、按钮点击等"触发型"场景 表格行操作、列表项自定义渲染等"渲染型"场景

二、完整示例对比

下面用同一个表格需求 ,分别写 $emit 版本和作用域插槽版本,可以直接复制运行,直观感受差异。

场景需求

实现一个表格,每行显示"姓名+操作按钮",点击按钮删除对应行数据。


版本1:使用 $emit 实现(耦合度高)
Plain 复制代码
<!-- 子组件:MyTable-emit.vue -->
<template>
  <table border="1" cellpadding="10">
    <tr>
      <th>姓名</th>
      <th>操作</th>
    </tr>
    <tr v-for="item in data" :key="item.id">
      <td>{{ item.name }}</td>
      <!-- 子组件固定渲染"删除按钮",并触发事件 -->
      <td>
        <button @click="$emit('delete', item.id)">删除</button>
      </td>
    </tr>
  </table>
</template>

<script>
export default {
  props: {
    data: { type: Array, required: true }
  }
}
</script>

<!-- 父组件:App.vue -->
<template>
  <div>
    <h3>$emit 版本(耦合度高)</h3>
    <MyTable-emit :data="list" @delete="handleDelete" />
  </div>
</template>

<script>
import MyTableEmit from './MyTable-emit.vue'
export default {
  components: { MyTableEmit },
  data() {
    return {
      list: [
        { id: 1, name: '张三' },
        { id: 2, name: '李四' },
        { id: 3, name: '王五' }
      ]
    }
  },
  methods: {
    handleDelete(id) {
      // 父组件处理删除逻辑
      this.list = this.list.filter(item => item.id !== id)
    }
  }
}
</script>

缺点:如果想把"删除按钮"改成"编辑按钮",必须修改子组件的代码,子组件和业务逻辑强绑定。


版本2:使用作用域插槽实现(灵活性高)
Plain 复制代码
<!-- 子组件:MyTable-slot.vue -->
<template>
  <table border="1" cellpadding="10">
    <tr>
      <th>姓名</th>
      <th>操作</th>
    </tr>
    <tr v-for="item in data" :key="item.id">
      <td>{{ item.name }}</td>
      <!-- 子组件只暴露数据,不渲染任何按钮 -->
      <td>
        <slot :row="item"></slot>
      </td>
    </tr>
  </table>
</template>

<script>
export default {
  props: {
    data: { type: Array, required: true }
  }
}
</script>

<!-- 父组件:App.vue -->
<template>
  <div>
    <h3>作用域插槽版本(灵活性高)</h3>
    <MyTable-slot :data="list">
      <!-- 父组件自定义操作按钮,直接使用子组件暴露的 row 数据 -->
      <template v-slot="{ row }">
        <!-- 想改按钮类型/样式/逻辑,只改父组件即可,子组件无需变动 -->
        <button @click="handleDelete(row.id)" style="margin-right: 5px;">删除</button>
        <button @click="handleEdit(row)">编辑</button>
      </template>
    </MyTable-slot>
  </div>
</template>

<script>
import MyTableSlot from './MyTable-slot.vue'
export default {
  components: { MyTableSlot },
  data() {
    return {
      list: [
        { id: 1, name: '张三' },
        { id: 2, name: '李四' },
        { id: 3, name: '王五' }
      ]
    }
  },
  methods: {
    handleDelete(id) {
      this.list = this.list.filter(item => item.id !== id)
    },
    handleEdit(row) {
      alert(`编辑:${row.name}`)
    }
  }
}
</script>

优点:子组件完全通用,父组件可以自由定义操作按钮(删除、编辑、查看等),甚至修改按钮样式、添加新按钮,无需改动子组件一行代码。

三、总结

  1. $emit ** 是"事件驱动"**:子组件触发事件,父组件响应处理,适合"触发逻辑"的场景,耦合度较高;

  2. 作用域插槽是"数据暴露":子组件只提供数据,父组件自定义渲染,适合"自定义内容"的场景,灵活性和复用性更高;

  3. 选型原则 :如果需要父组件自定义子组件内的内容 → 用作用域插槽;如果只是子组件通知父组件做某事 → 用 $emit

相关推荐
启山智软1 天前
【启山智软智能商城系统技术架构剖析】
java·前端·架构
一线大码1 天前
Java 使用国密算法实现数据加密传输
java·spring boot·后端
学不完的1 天前
ZrLog 高可用架构监控部署指南(Prometheus + Grafana)
linux·运维·架构·负载均衡·grafana·prometheus·ab测试
17(无规则自律)1 天前
【Linux驱动实战】:标准的按键控制LED驱动写法
linux·驱动开发·嵌入式硬件
我命由我123451 天前
Android Gradle - Gradle 自定义插件(Build Script 自定义插件、buildSrc 自定义插件、独立项目自定义插件)
android·java·java-ee·kotlin·android studio·android-studio·android runtime
Riu_Peter1 天前
【技术】Maven 配置 settings.xml 轮询下载
xml·java·maven
我命由我123451 天前
React Router 6 - 嵌套路由、路由传递参数
前端·javascript·react.js·前端框架·html·ecmascript·js
十六年开源服务商1 天前
2026年WordPress网站地图完整指南
java·前端·javascript
DA02211 天前
Linux驱动-I2C总线驱动
linux·c语言·linux驱动
Edward111111111 天前
3月17枚举
java·开发语言