Vue 学习笔记:组件通信(Props / 自定义事件)与插槽(Slot)全解析

作为 Vue 初学者,组件通信插槽是实现组件复用、解耦的核心技能。这篇笔记会把这两个知识点拆解成 "一看就懂" 的形式,既是自己的学习总结,也希望能帮到同样入门的同学~

一、组件通信:父子组件的数据传递

Vue 中组件是独立的,想要实现 "组件之间传数据",核心有两种场景:父传子(Props)子传父(自定义事件)

1. 父传子:Props 传递数据(单向数据流)

父组件通过props把数据传递给子组件,子组件只能 "读取" 不能 "直接修改"(Vue 的单向数据流规则)。

示例:父组件ComponentA向子组件ComponentB传值

vue

xml 复制代码
<!-- ComponentA.vue(父组件) -->
<template>
  <h3>ComponentA</h3>
  <!-- 第一步:在子组件标签上绑定要传递的数据 -->
  <ComponentB :title="title" :age="age" :names="names"/>
</template>

<script>
import ComponentB from "./ComponentB.vue"
export default {
  components: { ComponentB },
  data() {
    return {
      title: "父组件传递的标题",
      age: 20,
      names: ["iwen", "ime"]
    }
  }
}
</script>

vue

xml 复制代码
<!-- ComponentB.vue(子组件) -->
<template>
  <p>{{ title }}</p>
  <p>{{ age }}</p>
  <p v-for="(item,index) of names" :key="index">{{ item }}</p>
</template>

<script>
export default {
  // 第二步:子组件通过props选项声明接收的数据
  props: ["title", "age", "names"]
}
</script>

进阶:Props 的类型、默认值、必填校验 实际开发中,我们可以给props加 "类型限制、默认值、必填校验",让组件更健壮。

vue

xml 复制代码
<!-- ComponentB.vue(子组件) -->
<script>
export default {
  props: {
    // 限制title的类型可以是字符串、数字、数组、对象
    title: {
      type: [String, Number, Array, Object],
      required: true // 表示title是必填项
    },
    // 限制age的类型是数字,默认值为0
    age: {
      type: Number,
      default: 0
    },
    // 数组/对象的默认值必须用工厂函数返回
    names: {
      type: Array,
      default() {
        return ["默认名称"]
      }
    }
  }
}
</script>

2. 子传父:自定义事件($emit)传递数据

子组件通过this.$emit("事件名", 数据)触发自定义事件,父组件通过@事件名监听并接收数据。

示例:子组件Child向父组件ComponentEvent传值

vue

xml 复制代码
<!-- Child.vue(子组件) -->
<template>
  <h3>Child</h3>
  <button @click="clickEventHandle">传递数据给父组件</button>
</template>

<script>
export default {
  data() {
    return {
      msg: "子组件的私有数据~"
    }
  },
  methods: {
    clickEventHandle() {
      // 触发自定义事件,并传递数据msg
      this.$emit("someEvent", this.msg)
    }
  }
}
</script>

vue

xml 复制代码
<!-- ComponentEvent.vue(父组件) -->
<template>
  <h3>组件事件</h3>
  <!-- 监听子组件的someEvent事件,触发getHandle方法 -->
  <Child @someEvent="getHandle"/>
  <p>父组件接收的子组件数据:{{ message }}</p>
</template>

<script>
import Child from "./Child.vue"
export default {
  components: { Child },
  data() {
    return {
      message: ""
    }
  },
  methods: {
    getHandle(data) {
      // 接收子组件传递的数据,并赋值给message
      this.message = data
    }
  }
}
</script>

二、插槽(Slot):让组件更灵活的 "内容插槽"

插槽的作用是让父组件可以向子组件 "注入" 自定义内容 ,实现组件的灵活复用。Vue 的插槽分为三种:默认插槽、具名插槽、作用域插槽

1. 默认插槽:最简单的 "内容注入"

子组件用<slot></slot>预留一个 "内容坑位",父组件在子组件标签内写的内容会自动填入这个坑位。

示例:父组件App向子组件SlotsTow注入内容

vue

xml 复制代码
<!-- SlotsTow.vue(子组件) -->
<template>
  <h3>Slots续集</h3>
  <!-- 预留默认插槽的坑位 -->
  <slot></slot>
</template>

vue

xml 复制代码
<!-- App.vue(父组件) -->
<template>
  <SlotsTow>
    <!-- 父组件在这里写的内容,会被注入到子组件的slot中 -->
    <h3>我是父组件注入的内容</h3>
    <p>插槽让组件变得更灵活!</p>
  </SlotsTow>
</template>

<script>
import SlotsTow from "./components/SlotsTow.vue"
export default {
  components: { SlotsTow }
}
</script>

默认内容: 如果父组件没注入内容,子组件可以给插槽设置 "默认内容"。

vue

xml 复制代码
<!-- SlotsTow.vue(子组件) -->
<template>
  <h3>Slots续集</h3>
  <!-- 没内容时显示"默认内容" -->
  <slot>父组件没传内容,我是默认显示的文字~</slot>
</template>

2. 具名插槽:多个 "命名坑位" 的精准注入

当子组件需要多个插槽坑位 时,用name给每个插槽命名,父组件用v-slot:名称(或#名称)指定内容注入到哪个坑位。

示例:子组件SlotsAttrheadermain两个具名插槽

vue

xml 复制代码
<!-- SlotsAttr.vue(子组件) -->
<template>
  <div>
    <h3>具名插槽示例</h3>
    <!-- 命名为header的插槽 -->
    <slot name="header"></slot>
    <!-- 命名为main的插槽 -->
    <slot name="main"></slot>
  </div>
</template>

vue

xml 复制代码
<!-- App.vue(父组件) -->
<template>
  <SlotsAttr>
    <!-- 用v-slot:header指定内容注入到header插槽 -->
    <template v-slot:header>
      <h4>这是头部插槽的内容</h4>
    </template>
    <!-- 用#main指定内容注入到main插槽(#是v-slot的简写) -->
    <template #main>
      <p>这是主体插槽的内容</p>
    </template>
  </SlotsAttr>
</template>

<script>
import SlotsAttr from "./components/SlotsAttr.vue"
export default {
  components: { SlotsAttr }
}
</script>

3. 作用域插槽:子组件向父组件 "反向传值" 的插槽

子组件在插槽中绑定数据(slotProps),父组件可以 "接收并使用" 这些数据,实现 "子传父式的插槽内容定制"

示例:子组件SlotsAttr向父组件传递msgdemo数据

vue

xml 复制代码
<!-- SlotsAttr.vue(子组件) -->
<template>
  <div>
    <h3>作用域插槽示例</h3>
    <!-- 插槽中绑定数据(msg和demo) -->
    <slot name="header" :msg="slotMsg" :demo="slotDemo"></slot>
    <slot name="main" :msg="slotMsg" :demo="slotDemo"></slot>
  </div>
</template>

<script>
export default {
  data() {
    return {
      slotMsg: "子组件传递的消息",
      slotDemo: "子组件传递的演示数据"
    }
  }
}
</script>

vue

xml 复制代码
<!-- App.vue(父组件) -->
<template>
  <SlotsAttr>
    <!-- 接收子组件传递的slotProps,并使用其中的msg和demo -->
    <template #header="slotProps">
      <h4>头部插槽:{{ slotProps.msg }} - {{ slotProps.demo }}</h4>
    </template>
    <template #main="slotProps">
      <p>主体插槽:{{ slotProps.msg }} - {{ slotProps.demo }}</p>
    </template>
  </SlotsAttr>
</template>

<script>
import SlotsAttr from "./components/SlotsAttr.vue"
export default {
  components: { SlotsAttr }
}
</script>

渲染作用域: 插槽内容是在父组件的作用域 中定义的,所以可以直接访问父组件的数据;而插槽的slotProps是子组件传递的,父组件通过v-slot接收后才能使用。

三、总结:组件通信与插槽核心要点

知识点 核心作用 关键规则 / 技巧
Props(父传子) 父组件向子组件传递数据 子组件只能读不能直接改;支持类型、默认值、必填校验;数组 / 对象默认值需用工厂函数
自定义事件(子传父) 子组件向父组件传递数据 / 事件 子组件用this.$emit("事件名", 数据)触发;父组件用@事件名监听
默认插槽 父组件向子组件注入单个自定义内容 子组件用<slot></slot>预留坑位;父组件在子组件标签内写内容即可注入
具名插槽 父组件向子组件注入多个命名的自定义内容 子组件用name给插槽命名;父组件用v-slot:名称(或#名称)指定注入位置
作用域插槽 子组件向父组件传递数据,父组件定制插槽内容 子组件在插槽中绑定数据(slotProps);父组件用v-slot="slotProps"接收并使用

掌握这些知识点后,Vue 组件的复用性和灵活性会大幅提升~ 后续再深入学习 Vuex、Pinia 等状态管理工具,就能应对更复杂的组件通信场景了。

相关推荐
UIUV2 小时前
Ajax 数据请求学习笔记
前端·javascript·代码规范
FogLetter2 小时前
手写useInterval:告别闭包陷阱,玩转React定时器!
前端·react.js
神秘的猪头2 小时前
Vibe Coding 实战教学:用 Trae 协作开发 Chrome 扩展 “Hulk”
前端·人工智能
小时前端2 小时前
当递归引爆调用栈:你的前端应用还能优雅降落吗?
前端·javascript·面试
张可爱2 小时前
20251112-问题排查与复盘
前端
ZKshun2 小时前
WebSocket指南:从原理到生产环境实战
前端·websocket
不说别的就是很菜2 小时前
【前端面试】Git篇
前端·git
欧阳码农2 小时前
盘点这两年我接触过的副业赚钱赛道,对于你来说可能是信息差
前端·人工智能·后端
恋猫de小郭2 小时前
Dart 3.10 发布,快来看有什么更新吧
android·前端·flutter