在 Vue 2 的组件树中,数据与事件的流动决定了整个应用的架构质量。本文按照通信距离由近及远,系统梳理 Vue 2 官方与社区沉淀的全部手段,并给出选型建议。
一、父子通信:官方 API 的黄金三角
- prop 向下,event 向上
- prop 是单向数据流,父组件通过属性把数据注入子组件;子组件内部只读,不直接修改。
- emit 让子组件反向通知父组件,触发父级事件回调,实现"子 → 父"通信。
- 经典组合:
v-bind
传值,v-on
捕获事件。
-
class 与 style 的隐式合并
当父组件把
class
或style
绑定到子组件根元素时,Vue 会自动与子组件自身的同名属性进行合并,而非覆盖。这在 UI 组件封装时尤为实用,可让父级快速微调子级样式而不破坏内部结构。 -
attribute 透传与 attrs
未被
props
显式声明的属性会落入$attrs
,并自动附加到子组件根元素。若子组件设置inheritAttrs: false
,属性不再挂到 DOM,但$attrs
依旧可用,便于高阶组件或封装第三方库时做属性代理。 -
修饰符与语法糖
.native
修饰符:把事件直接绑定到子组件根节点,适用于需要监听原生 DOM 事件的场景。sync
修饰符:语法糖,展开为@update:xxx
事件,允许多个 prop 实现双向绑定。v-model
:底层即 prop + 自定义事件,官方推荐在表单类组件中使用。
- ref、parent、children
ref
让父组件拿到子组件实例,可直接调用子组件方法或访问数据。$parent
/$children
提供实例遍历,但破坏了单向数据流,建议仅在高阶组件或调试工具中使用,业务代码应尽量避免。
二、跨层级通信:Provide / Inject 的优雅穿透
当组件层级过深,逐层传递 prop 会导致"钻透地狱"。Vue 2 提供一对官方 API:
- provide 在祖先组件中发布数据或方法;
- inject 在任意后代组件中按需接收。
provide/inject 不是响应式系统替代品,而是上下文注入。若 provide 的值是对象,其内部属性依旧保持响应式,因此可用来共享只读全局状态或主题配置。
三、全局通信:路由、Vuex、EventBus 三剑客
-
路由驱动
Vue Router 把地址栏变成全局状态机。任何组件只要监听
$route
,即可在路由变化时自动刷新视图。典型场景:点击router-link
切换页面,router-view
自动渲染对应组件。 -
Vuex:集中式仓库
当应用规模扩大,父子、祖孙通信已无法承载复杂业务时,引入 Vuex:
- 所有组件共享单一 Store;
- 通过
mapState
/mapGetters
/mapMutations
读写; - 严格模式保证状态变更可追溯。
Vuex 的核心价值是可预测性,而非单纯的全局变量。
- EventBus:轻量级事件总线
对于中小型项目或一次性全局事件,可用空 Vue 实例充当事件中心:
js
// bus.js
export const bus = new Vue()
// 组件 A
bus.$emit('loginSuccess', user)
// 组件 B
bus.$on('loginSuccess', user => { ... })
优点是零依赖、上手快;缺点是事件满天飞时难以追踪,大型项目慎用。
四、简易全局状态:Store 模式
若业务复杂度介于 EventBus 与 Vuex 之间,可手动维护一个响应式对象作为全局 store:
js
// store.js
export default Vue.observable({
loginUser: null,
settings: {}
})
任何组件导入后皆可读写,无需额外插件。适合后台管理系统、配置面板等场景。
五、选型与建议
- 父子通信优先 prop + event;
- 跨层级优先 provide/inject,避免逐层传递;
- 全局共享根据复杂度递进:Store 模式 < EventBus < Vuex;
- 调试阶段可临时使用
$parent
、$children
,上线前务必重构。
掌握这些通信机制后,你不仅能写出更优雅的 Vue 2 应用,也能在面试中条理清晰地回答"Vue 组件通信有哪些方式"------从 prop 到 Vuex,从 EventBus 到 provide/inject,全部信手拈来。