Vue 3 组件通信,别只会用 Props 和 Emits 了,你想知道的都在这里
Vue 3 组件通信,别只会用 Props 和 Emits 了,你想知道的都在这里
作为前端开发工程师,组件通信是Vue开发中绕不开的话题。虽然Props和Emits是最基础的通信方式,但在实际项目中,我们经常会遇到更复杂的场景。今天,我将带你全面了解Vue 3中14种组件通信方式,让你的开发技能更上一层楼!
📋 目录
- 父子组件通信(6种方式)
- 兄弟组件通信(3种方式)
- 跨层级通信(2种方式)
- 全局通信(2种方式)
- 特殊场景通信(3种方式)
- 完整对比总结表
一、父子组件通信(6种方式)
1️⃣ Props(父 → 子)
最基础的父传子方式,遵循单向数据流原则。
适用场景:简单的父子数据传递
2️⃣ Emit(子 → 父)
子组件向父组件传递数据或触发事件。
适用场景:子组件需要通知父组件状态变化
3️⃣ v-model(双向绑定)
Vue 3中v-model的语法糖,实现父子组件数据双向绑定。
单值绑定(Vue 3.4+)
多个v-model绑定
适用场景:表单组件、需要双向数据同步的场景
4️⃣ ref + defineExpose(父调用子组件方法)
父组件直接调用子组件的方法或访问子组件数据。
适用场景:父组件需要直接操作子组件内部方法或数据
5️⃣ $attrs(属性透传)
透传父组件传递的、但子组件未声明的属性到更深层组件。
适用场景:
- 封装第三方组件库
- 高阶组件开发
- 多层组件嵌套中的属性传递
6️⃣ 作用域插槽(子传父数据)
子组件向父组件传递数据,由父组件决定如何渲染。
用户名: {{ user.name }}
年龄: {{ user.age }}
{{ user.name }}
适用场景:
- 列表组件(如Table、List)
- 需要父组件自定义渲染逻辑的场景
二、兄弟组件通信(3种方式)
7️⃣ 共同父组件中转
通过父组件作为桥梁,实现兄弟组件通信。
适用场景:兄弟组件数量较少,逻辑简单
8️⃣ mitt(事件总线)
轻量级的事件发布/订阅库,实现任意组件通信。
安装和配置
npm install mitt
// eventBus.js
import mitt from 'mitt'
export const emitter = mitt()
使用示例
适用场景:
- 跨层级组件通信
- 不想引入完整状态管理的轻量级场景
- 临时性通信需求
9️⃣ Pinia(状态管理)
Vue官方推荐的状态管理库,替代Vuex。
安装和配置
npm install pinia
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.mount('#app')
创建Store
// stores/userStore.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useUserStore = defineStore('user', () => {
// state
const userInfo = ref({ name: '', age: 0 })
const token = ref('')
// getters
const isLogin = computed(() => !!token.value)
// actions
const setUserInfo = (data) => {
userInfo.value = data
}
const login = async (username, password) => {
// 模拟登录
token.value = 'xxx-token'
userInfo.value = { name: username, age: 25 }
}
const logout = () => {
token.value = ''
userInfo.value = { name: '', age: 0 }
}
return {
userInfo,
token,
isLogin,
setUserInfo,
login,
logout
}
})
组件中使用
适用场景:
- 全局状态共享(用户信息、购物车等)
- 复杂业务逻辑的状态管理
- 需要持久化或服务端同步的场景
三、跨层级通信(2种方式)
🔟 provide / inject(依赖注入)
祖先组件向任意后代组件注入数据,无需逐层传递。
适用场景:
- 主题配置、语言设置等全局配置
- 深层嵌套组件的数据共享
- 避免props drilling(属性逐层传递)
1️⃣1️⃣ $root(访问根实例)
通过根实例进行全局通信(不推荐,仅作了解)。
// 在任何组件中
const app = getCurrentInstance().appContext.app
app.config.globalProperties.$globalData = '全局数据'
注意:Vue 3中不推荐使用此方式,建议使用provide/inject或Pinia。
四、全局通信(2种方式)
1️⃣2️⃣ localStorage / sessionStorage
利用浏览器本地存储实现组件间通信。
// 存储数据
localStorage.setItem('userData', JSON.stringify({ name: '张三' }))
// 读取数据
const userData = JSON.parse(localStorage.getItem('userData'))
// 监听storage变化
window.addEventListener('storage', (e) => {
if (e.key === 'userData') {
console.log('数据变化:', e.newValue)
}
})
适用场景:
- 需要持久化的数据
- 跨页面/标签页通信
- 离线数据存储
1️⃣3️⃣ app.config.globalProperties
在应用级别添加全局属性。
// main.js
const app = createApp(App)
app.config.globalProperties.$api = {
getUser: () => { /* ... */ },
saveData: () => { /* ... */ }
}
app.mount('#app')
适用场景:
- 全局工具函数
- 第三方库实例(如axios)
- 全局配置
五、特殊场景通信(3种方式)
1️⃣4️⃣ Teleport(传送门)
将组件渲染到DOM树的其他位置。
主内容
这是一个模态框
适用场景:
- 模态框、弹窗
- 需要脱离父组件样式影响的组件
- 固定定位组件
1️⃣5️⃣ 动态组件通信
使用动态切换组件时的通信。
适用场景:
- 选项卡切换
- 步骤向导
- 动态表单
1️⃣6️⃣ 自定义事件修饰符
为自定义组件添加v-model修饰符支持。
📊 完整对比总结表
| 通信方式 | 适用场景 | 优点 | 缺点 | 推荐指数 |
|---|---|---|---|---|
| Props | 父→子 | 简单、官方推荐、类型安全 | 只能父子使用 | ⭐⭐⭐⭐⭐ |
| Emit | 子→父 | 简单、符合单向数据流 | 只能父子使用 | ⭐⭐⭐⭐⭐ |
| v-model | 双向绑定 | 语法简洁、语义清晰 | 需要配合props/emits | ⭐⭐⭐⭐ |
| ref+expose | 父调子方法 | 直观、灵活 | 只能父对子 | ⭐⭐⭐⭐ |
| $attrs | 属性透传 | 避免中间层冗余代码 | 可读性稍差 | ⭐⭐⭐ |
| 作用域插槽 | 子传父数据 | 灵活、父组件控制渲染 | 语法稍复杂 | ⭐⭐⭐⭐ |
| mitt | 任意组件 | 轻量、解耦 | 需手动管理订阅 | ⭐⭐⭐⭐ |
| Pinia | 全局状态 | 集中管理、类型安全 | 学习成本 | ⭐⭐⭐⭐⭐ |
| provide/inject | 跨层级 | 避免props drilling | 松散耦合 | ⭐⭐⭐⭐ |
| localStorage | 持久化 | 跨页面、持久化 | 同步问题 | ⭐⭐⭐ |
| Teleport | DOM位置 | 灵活渲染位置 | 特定场景 | ⭐⭐⭐ |
💡 实战建议
选择通信方式的原则
- 优先使用Props/Emit:简单场景首选,符合Vue设计哲学
- 避免过度使用全局状态:不是所有数据都需要放入Pinia
- 考虑组件复用性:选择通用性更强的通信方式
- 关注性能:避免不必要的响应式数据传递
常见误区
❌ 滥用provide/inject:不要用它替代props,它更适合配置类数据
❌ 过度依赖事件总线:mitt适合轻量级场景,复杂业务用Pinia
❌ 忘记清理事件监听:使用mitt时记得在onUnmounted中移除监听
❌ 暴露过多子组件内部:defineExpose只暴露必要的API
🎯 总结
Vue 3提供了丰富的组件通信方式,每种方式都有其适用场景:
- 简单父子通信:Props + Emit
- 双向绑定:v-model
- 父调子方法:ref + defineExpose
- 跨层级通信:provide/inject
- 全局状态:Pinia
- 轻量级任意通信:mitt
- 灵活渲染:作用域插槽
掌握这些通信方式,能让你在面对不同业务场景时游刃有余。记住,没有最好的方式,只有最合适的方式!
互动话题:你在项目中遇到过哪些复杂的组件通信场景?是如何解决的?欢迎在评论区分享你的经验!🚀
本文基于Vue 3.4+版本编写,代码示例均经过实际测试。如有疑问,欢迎交流讨论!
原文地址: https://www.cveoy.top/t/topic/qGv9 著作权归作者所有。请勿转载和采集!