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 动手改
- TaskItem 加
highlightprop,置顶项高亮 - TaskItem 加「完成」按钮,
emits: ['remove', 'done'],父组件对完成事件做个 console.log - 在 7.2 页面的 ul 下面加一段文字显示
还剩 {{ tasks.length }} 项,删一个数字就少一个
7.6 小结
- 免构建组件里:
props收数据、emits声明上报的事件、$emit('remove')喊话 - 父组件用
@remove="..."接住 - props 只读;数据改动放定义它的地方
- 全局状态以后用 Pinia,先不急
下一章:综合练习:Vue 版待办清单 🐸