前言
在 Vue 中,组件通信有多种方式,适用于不同场景(父子组件、兄弟组件、跨级组件等)。以下是完整的组件传值方法总结,仅供概览参考:
一、父子组件通信
1. Props(父 → 子)
父组件通过属性传递数据,此方式单向数据流,子组件的数据会随着父的更改而响应式的得到更新
<!-- 父组件 -->
<Child :title="parentData" />
// 子组件
export default {props: ['title']
}
2. 自定义事件(子 → 父)
子组件通过 $emit
绑定一个自定义事件并触发,当这个事件被执行的时候就会将参数传递给父组件,而父组件通过v-on
监听并接收
<!-- 父组件 -->
<Child @custom-event="handleEvent" />
// 子组件
this.$emit('custom-event', data)
3. v-model
/ .sync
(双向绑定)
-
Vue 2: 使用
.sync
修饰符<!-- 父组件 --> <Child :value.sync="data" /> <!-- 子组件触发更新:this.$emit('update:value', newValue) -->
-
Vue 3: 默认支持多个
v-model
<Child v-model:title="pageTitle" /> <!-- 子组件触发:emit('update:title', newValue) -->
4. $refs
(访问子组件实例)
父组件直接调用子组件方法/数据
<Child ref="childRef" />
this.$refs.childRef.childMethod();
二、兄弟组件通信
1. 事件总线(Event Bus)
其原理就是:事件订阅发布,eventBus
又称为事件总线; 缺点:多人协同开发场景维护困难
-
创建全局事件中心:
// eventBus.js import { mitt } from 'mitt'; // Vue 3 推荐使用 mitt 库 export const bus = mitt();
-
组件 A 发送事件:
bus.emit('event-name', data);
-
组件 B 接收事件:
bus.on('event-name', (data) => { ... });
2. 通过共同父组件中转
子组件 A → 父组件(事件)→ 子组件 B(Props)
三、跨层级通信
1. Provide / Inject
-
祖先组件提供数据:
export default {provide() {return { theme: this.themeData };} }
-
后代组件注入数据:
export default {inject: ['theme'] }
-
Vue 3 响应式方案:
import { provide, ref } from 'vue'; const data = ref('value'); provide('key', data);
2. $attrs
/ $listeners
(透传)
$attrs
包含了父作用域中不被认为 (且不预期为) props 的特性绑定 (class 和 style 除外),并且可以通过 v-bind=“$attrs” 传入内部组件。当一个组件没有声明任何 props 时,它包含所有父作用域的绑定 (class 和 style 除外)。$listeners
包含了父作用域中的 (不含 .native 修饰符) v-on 事件监听器。它可以通过 v-on=“$listeners” 传入内部组件。它是一个对象,里面包含了作用在这个组件上的所有事件监听器,相当于子组件继承了父组件的事件。
-
父组件传递未声明的 Props/事件:
<GrandChild v-bind="$attrs" v-on="$listeners" />
-
Vue 3 合并
$attrs
和$listeners
,直接使用v-bind="$attrs"
。
四、全局状态管理
1. Vuex(Vue 2 官方方案)
// 组件中获取/修改状态
this.$store.state.data;
this.$store.commit('mutation', value);
2. Pinia(Vue 3 官方推荐)
// store/user.js
export const useUserStore = defineStore('user', {state: () => ({ name: 'John' }),actions: { updateName(name) { this.name = name; } }
});// 组件中使用
import { useUserStore } from '@/stores/user';
const store = useUserStore();
store.name; // 获取
store.updateName('Alice'); // 修改
五、其他方式
1. 插槽作用域(父 ↔ 子)
-
子组件通过插槽暴露数据:
<!-- 子组件 --> <slot :item="itemData"></slot>
-
父组件使用:
<Child v-slot="slotProps">{{ slotProps.item }} </Child>
2. 浏览器存储
localStorage
/ sessionStorage
+ window.addEventListener('storage')
监听变化。
3. $children | $parent (vue3已弃用)
通过 $parent 访问到的是上一级父组件的实例,可以使用 $root 来访问根组件的实例在组件中使用$children拿到的是所有的子组件的实例,它是一个数组,并且是无序的在根组件 #app 上拿 $parent 得到的是 new Vue()的实例,在这实例上再拿 $parent 得到的是undefined,而在最底层的子组件拿 $children 是个空数组$children 的值是数组,而 $parent是个对象删除线格式
选择建议
场景 | 推荐方式 |
---|---|
父子组件简单传值 | Props / 自定义事件 |
父子组件双向绑定 | v-model (Vue 3) / .sync (Vue 2) |
跨层级/深嵌套组件 | Provide / Inject |
兄弟/任意组件通信 | 事件总线 / Vuex/Pinia |
全局状态管理(复杂应用) | Pinia (Vue 3) / Vuex (Vue 2) |
透传属性/事件 | $attrs / v-bind="$attrs" |
注意:
- Vue 3 移除了
$children
和$listeners
(合并到$attrs
)- 事件总线在 Vue 3 需用第三方库(如
mitt
)- 优先使用单向数据流(Props + 事件),避免直接修改父组件数据
总结
实际使用中应该根据项目规模选择合适的方案,小型项目可用 Props/事件总线,中大型项目推荐 Pinia/Vuex。