Vue 2 组件数据流动全景图:15 种通信方案详解与示例
在 Vue 2 中,组件是 UI 的最小单元,而数据流动则是组件之间协作的"血液"。本文用 15 种方式演示如何安全、优雅地把数据从一个组件送到另一个组件,并给出可直接粘贴运行的代码片段。
一、父子组件通信
1. props / $emit
最常见的"父→子"与"子→父"组合。
// Parent.vue
<template>
<counter :initial="num" @add="inc"/>
</template>
<script>
export default {
data: () => ({ num: 0 }),
methods: { inc(v) { this.num += v } }
}
</script>
// Counter.vue
<template>
<button @click="$emit('add', 1)">+</button>
</template>
<script>
export default { props: { initial: Number } }
</script>
2. .sync 语法糖
把上例的 v-bind:page.sync="page" 与 this.$emit('update:page', newVal) 组合即可实现"双向绑定"。
3. v-model
自定义组件也可使用 v-model,只需声明 model: { prop: 'value', event: 'input' }。
4. ref
// Parent.vue
<child ref="c"/>
mounted() {
this.$refs.c.reset(); // 直接调用子组件方法
}
5. $children / $parent
通过索引访问子实例,或子组件通过 this.$parent.xxx 访问父实例。不推荐在复杂场景使用,因为顺序不可预期。
二、跨级通信
6. $attrs / $listeners
在多层嵌套时,把父级属性/事件一次性透传到孙组件,避免"逐层声明"。
<grand-child v-bind="$attrs" v-on="$listeners"/>
7. provide / inject
适合主题、国际化等"全局"常量或方法的注入。
// Ancestor.vue
provide() {
return { theme: this.themeColor }
}
// DeepDescendant.vue
inject: ['theme']
三、兄弟或任意组件通信
8. EventBus(中央事件总线)
// bus.js
import Vue from 'vue'
export default new Vue()
// 发送方
import Bus from './bus'
Bus.$emit('login', user)
// 接收方
import Bus from './bus'
Bus.$on('login', user => { ... })
9. Vuex
当项目规模变大,使用集中式 Store 统一管理状态。
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: { count: 0 },
mutations: { plus(state) { state.count++ } }
})
// 组件内
methods: {
...mapMutations(['plus'])
}
10. $root
直接访问根实例上的数据或方法,适合极小型应用或插件。
四、内容分发与路由
11. slot(插槽)
// Card.vue
<div class="card">
<slot name="header"/>
<slot/>
</div>
// 使用
<card>
<template #header>标题</template>
正文
</card>
12. 作用域插槽
子组件把内部数据暴露给父级模板。
<slot :user="user"></slot>
13. 路由传参
- 路径参数:
/user/:id→this.$route.params.id - 查询参数:
/user?id=1→this.$route.query.id - 命名路由 + params:不会出现在 URL,刷新丢失。
五、轻量级响应式仓库
14. Vue.observable
在不需要 Vuex 的小项目里,用 Vue.observable 创建一个最小全局 Store。
// store.js
import Vue from 'vue'
export const state = Vue.observable({ count: 0 })
export const setCount = n => state.count = n
// 组件
import { state, setCount } from './store'
computed: { count: () => state.count }
methods: { setCount }
六、小结速查表
| 场景 | 推荐方案 |
|---|---|
| 父→子 | props / v-model / .sync |
| 子→父 | $emit / v-on |
| 隔代 | provide/inject 或 $attrs/$listeners |
| 兄弟 | EventBus / Vuex |
| 全局 | Vuex / $root / Vue.observable |
根据团队规模、状态复杂度选择最合适的通信方式,才能保持代码长期可维护。