Vue 的父子组件通信有两条路:最基础的
props
+ 事件机制,以及更"灵巧"的.sync
修饰符。它们看起来都能让子组件拿到父的数据,但本质上,两者体现的是两种完全不同的设计哲学。
一、props:数据的"只读快照"
在 Vue 中,props
是子组件获取父组件状态的唯一合法入口。
js
<!-- 父 -->
<Child :title="pageTitle" />
<!-- 子 -->
props: { title: String }
当父组件中的 pageTitle
改变时,title
会同步刷新。
但反过来,子组件不能修改 props
。
如果你写了:
js
this.title = 'New Title';
Vue 会警告:
Avoid mutating a prop directly since the value will be overwritten...
🔹为什么?
因为 Vue 设计的是 单向数据流 (One-Way Data Flow) :
父组件掌控状态,子组件只能使用,不可反写。
这样可以保证数据变化的来源是唯一、可追踪的。
二、.sync:事件驱动的"有序双向"
Vue 2.3 引入的 .sync
修饰符是一种语法糖,用于在不破坏单向数据流的前提下,实现"父子同步"。
js
<!-- 父 -->
<Child :value.sync="username" />
<!-- 等价于 -->
<Child
:value="username"
@update:value="val => username = val"
/>
子组件内部写法:
js
this.$emit('update:value', newValue);
于是,数据流就变成了:
js
父 (username)
↓ props
子 (value)
↑ $emit('update:value')
父更新
Vue 并没有真的让数据"双向",而是显式地走了一次事件上抛 。
这比直接改 prop 更安全、更清晰。
三、哲学上的分歧:控制权在谁手里?
机制 | 数据归属 | 修改方式 | 设计理念 |
---|---|---|---|
props | 父组件 | 父自己改 | 单向、可追踪 |
.sync | 父组件 | 子发事件→父改 | 双向意图、事件驱动 |
props
是 "我给你数据,你随便展示";.sync
是 "我把数据借你用,但你改时要告诉我"。
四、为什么要 .sync?
在一些场景下,props
太单纯:
- 例如子组件内有表单、筛选条件、分页状态;
- 用户修改后,需要反映到父组件的数据模型上;
- 若完全用
props
+ 自定义事件,会写出很多重复模板。
.sync
把这个模式统一成约定俗成的写法:
js
<!-- 父 -->
<ListPanel :filters.sync="queryParams" />
<!-- 子 -->
this.$emit('update:filters', { page: 2, size: 20 })
优势:
- 少写模板代码;
- 事件名统一(
update:*
); - 数据修改路径清晰,符合单向流;
- IDE/TypeScript 补全更友好。
五、.sync 的延伸:对象同步与 computed get/set
如果我们想同步的不止一个字段,而是一整份对象,可以进一步用 computed 的 get/set:
js
// 父组件
computed: {
formData: {
get() {
return { name: this.name, age: this.age };
},
set(v) {
this.name = v.name;
this.age = v.age;
},
},
}
js
<!-- 父 -->
<Child :form.sync="formData" />
<!-- 子 -->
this.$emit('update:form', { ...this.form, name: 'Alice' })
这样就能优雅地把多个 prop 映射成一个可同步对象,在复杂表单中非常常见。
六、设计思想:Vue 的"受控组件"哲学
Vue 的 .sync
和 update:*
事件,本质上类似 React 的 Controlled Component 模型:
父组件持有数据状态,子组件只负责发出状态变化意图。
这体现了 Vue 一贯的三条设计原则:
- 单向数据流
数据总是从上到下传递,修改通过事件显式上抛。 - 显式通信
不做"神秘双向",而是通过固定命名的事件让数据流清晰可见。 - 约定优于配置
update:propName
约定了组件之间的通用协议,统一写法、统一心智。
七、用法建议总结
场景 | 推荐写法 | 说明 |
---|---|---|
展示型组件 | props |
只读,父控制全部逻辑 |
输入型组件(单值) | .sync 或 v-model |
统一事件 update:value |
表单类组件(多字段) | .sync + computed get/set |
打包成对象同步 |
内部状态无需外部感知 | 普通 data | 子自己管理,不透出 |
八、结语
.sync
是让单向数据流更自然、更低摩擦地闭环。
它与 props
的关系,就像"有读写权限的接口"与"只读视图":
props
保证了数据的纯净;.sync
则让交互更高效,但仍保持可追踪。
Vue 的哲学是让修改有迹可循。
💡 总结
props
是下行数据流;.sync
是上行的事件语法糖。前者强调稳定,后者强调交互。
Vue 通过它们,构建了一个「既能约束,又能表达」的组件通信体系。