🐸 青蛙编程网
首页 / 前端 / Vue / 7. 组件通信

7. 组件通信

这是什么:组件通信 = 组件之间传数据的方式,规则一句话:props 向下传,事件向上送。

为什么用:上一章注册的组件自己没有数据,文字得从父组件拿;将来点「删除」,也总得告诉真正管数据的父组件一声。

如何用:本章用 props 往下传文字、用 $emit 往上报事件,先给列表项加一个删除按钮试试。

7.1 props:父 → 子(传数据)

<script>
const Child = {
  props: {
    title: { type: String, default: '默认标题' },
    count: { type: Number, required: true }
  },
  template: `<p>{{ title }}(数量 {{ count }})</p>`
}
</script>
<!-- 父组件使用 -->
<child title='公告栏' :count='10'></child>
<child :count='5'></child>              <!-- title 用默认值 -->

要点:

  • 属性名用 camelCase,模板里写 kebab-case(:post-count)
  • props 只读!子组件不许改它——改了 Vue 会警告

7.2 emit:子 → 父(报事件)

预期场景:子组件里点删除按钮,实际数据在父组件手里 → 子组件「喊话」,父组件处理:

<!-- 子组件 TaskItem -->
<script>
const TaskItem = {
  props: ['text'],
  emits: ['remove'],   // 声明我会发哪些事件
  template: `<li>
    {{ text }}
    <button @click="$emit('remove')">删除</button>
  </li>`
}
</script>

完整页面(可直接跑,点删除试试):

<!DOCTYPE html>
<html lang='zh-CN'>
<head>
  <meta charset='utf-8'>
  <title>组件通信</title>
  <script src='https://registry.npmmirror.com/vue/3.5.44/files/dist/vue.global.prod.js'></script>
</head>
<body>
  <div id='app'>
    <ul>
      <task-item
        v-for='(t, i) in tasks'
        :key='t.id'
        :text='t.text'
        @remove='removeAt(i)'
      ></task-item>
    </ul>
  </div>

  <script>
    const { createApp, ref } = Vue

    // 子组件:只负责「喊话」,不管数据
    const TaskItem = {
      props: ['text'],
      emits: ['remove'],
      template: `<li>
        {{ text }}
        <button @click="$emit('remove')">删除</button>
      </li>`
    }

    createApp({
      components: { TaskItem },
      setup() {
        const tasks = ref([
          { id: 1, text: '学 Vue' },
          { id: 2, text: '写组件' },
          { id: 3, text: '过考试' }
        ])
        function removeAt(i) {
          tasks.value.splice(i, 1)     // 父组件收事件后做真事
        }
        return { tasks, removeAt }
      }
    }).mount('#app')
  </script>
</body>
</html>

上面页面里 @remove='removeAt(i)' 用了 v-for 的 index:子组件 $emit('remove') 喊话 → 父组件在标签上写 @remove="..." 接住 → 父组件调用 removeAt(i) 对数据动真格。

7.3 数据流向示意

[parent state]
   ↓ props: text
[TaskItem 显示]
   ↑ emit: remove
[父组件改 tasks]

口诀:props 是「给你」的,emit 是「听我说」的。改数据永远在持有者(父)那边。

7.4 跨层级共享数据(先知道就好)

层层 props 传数据会疲劳,社区常用 Pinia 状态库统一管理跨页面数据。入门先不管,等你写出三层传参再回来用它。

7.5 动手改

  1. TaskItem 加 highlight prop,置顶项高亮
  2. TaskItem 加「完成」按钮,emits: ['remove', 'done'],父组件对完成事件做个 console.log
  3. 在 7.2 页面的 ul 下面加一段文字显示 还剩 {{ tasks.length }} 项,删一个数字就少一个

7.6 小结

  • 免构建组件里:props 收数据、emits 声明上报的事件、$emit('remove') 喊话
  • 父组件用 @remove="..." 接住
  • props 只读;数据改动放定义它的地方
  • 全局状态以后用 Pinia,先不急

下一章:综合练习:Vue 版待办清单 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。