当前位置: 首页 > news >正文

组件通信与注册

文章目录

  • 前言
  • 一、通信方式总览
    • 1.1 选型指南
  • 二、Props / Emit(父子通信)
    • 2.1 单向数据流
    • 2.2 v-model 本质
    • 2.3 常见场景
  • 三、provide / inject(跨层级通信)
    • 3.1 基本用法
    • 3.2 响应式 provide
    • 3.3 应用场景
    • 3.4 易混淆点
  • 四、事件总线 mitt
    • 4.1 Vue 3 的变化
    • 4.2 封装与使用
    • 4.3 适用场景
  • 五、Pinia / Vuex(全局状态)
    • 5.1 何时使用
    • 5.2 Pinia 基本用法
    • 5.3 与其他方式对比
  • 六、组件注册方式
    • 6.1 全局注册
    • 6.2 局部注册
    • 6.3 异步组件注册
    • 6.4 全局 vs 局部
  • 七、通信方式对比总结
  • 八、面试聚焦
    • 8.1 Props 单向数据流
    • 8.2 provide/inject 响应式
    • 8.3 全局注册无法 Tree-shaking
    • 8.4 Vue 3 事件总线
  • 九、易混淆点
  • 十、思考与练习
  • 总结

前言

组件化开发的核心问题之一,就是组件之间如何传递数据和触发行为。Vue 提供了多种通信方式,本篇会讲清楚:

  • Props / Emit(父子通信)
  • provide / inject(跨层级通信)
  • 事件总线 mitt
  • Pinia / Vuex(全局状态)
  • 组件注册方式(全局 / 局部 / 异步)

一、通信方式总览

1.1 选型指南

方式适用场景关系
Props / Emit父子数据传递、子通知父直接父子
provide / inject主题、语言包、表单上下文祖孙跨层级
mitt(事件总线)兄弟组件、无关联组件任意组件
Pinia / Vuex用户状态、权限、购物车全局共享
// 选型原则:// 1. 能用 Props/Emit 解决的,优先用 Props/Emit(数据流清晰)// 2. 跨多层级透传 → provide/inject// 3. 无关联组件 → mitt 或 Pinia// 4. 多处共享的全局状态 → Pinia

二、Props / Emit(父子通信)

2.1 单向数据流

<!-- 父组件 Parent.vue --> <script setup> import { ref } from 'vue' import Child from './Child.vue' const count = ref(0) const handleChange = (val) => { count.value = val // 父组件修改数据 } </script> <template> <Child :count="count" @change="handleChange" /> </template>
<!-- 子组件 Child.vue --> <script setup> const props = defineProps({ count: { type: Number, default: 0 } }) const emit = defineEmits(['change']) const increment = () => { // ❌ 不能直接修改 props // props.count++ // ✅ 通过 emit 通知父组件 emit('change', props.count + 1) } </script> <template> <button @click="increment">{{ count }}</button> </template>

2.2 v-model 本质

<!-- v-model 是 Props + Emit 的语法糖 --> <MyInput v-model="text" /> <!-- 等价于 --> <MyInput :modelValue="text" @update:modelValue="text = $event" /> <!-- 多个 v-model --> <MyForm v-model:name="name" v-model:age="age" />

2.3 常见场景

// 1. 父传配置:列表组件接收 items 和 loading<List:items="list":loading="loading"/>// 2. 子通知父:表单提交后 emit submit 事件// emit('submit', formData)// 3. 分页:子组件 emit page-change,父组件加载数据// emit('page-change', page)

三、provide / inject(跨层级通信)

3.1 基本用法

// 祖先组件import{provide,ref}from'vue'consttheme=ref('dark')provide('theme',theme)// 后代组件(任意层级)import{inject}from'vue'consttheme=inject('theme','light')// 第二个参数是默认值

3.2 响应式 provide

// ❌ 默认不是响应式:传递普通值provide('count',0)// 后代无法感知变化// ✅ 传递 ref 或 reactive 实现响应式constcount=ref(0)provide('count',count)// 后代组件constcount=inject('count')// count 变化时,后代视图自动更新

3.3 应用场景

// 1. 主题配置provide('theme',{color:'primary',size:'medium'})// 2. 国际化provide('locale',locale)// 3. 表单上下文(Form → FormItem)provide('formContext',{rules,validate})// 4. 全局 HTTP 实例app.provide('http',axios.create({baseURL:'/api'}))

3.4 易混淆点

// 1. 多个祖先 provide 同名 key → 取最近祖先的值// 2. inject 可指定默认值,找不到 provider 不会报错// 3. app.provide 应用级注入,任何组件都可 inject// 4. 过度使用会导致数据流难追踪,简单场景优先 Props

四、事件总线 mitt

4.1 Vue 3 的变化

// Vue 2:实例方法constbus=newVue()bus.$on('message',handler)bus.$emit('message',data)bus.$off('message',handler)// Vue 3:$on/$off/$once 已移除,使用 mittimportmittfrom'mitt'constbus=mitt()bus.on('message',(data)=>console.log(data))bus.emit('message',{text:'Hello'})bus.off('message',handler)

4.2 封装与使用

// utils/eventBus.jsimportmittfrom'mitt'exportconsteventBus=mitt()// 组件 A:发送import{eventBus}from'@/utils/eventBus'eventBus.emit('refresh-list')// 组件 B:接收import{onMounted,onUnmounted}from'vue'import{eventBus}from'@/utils/eventBus'consthandler=()=>fetchList()onMounted(()=>eventBus.on('refresh-list',handler))onUnmounted(()=>eventBus.off('refresh-list',handler))

4.3 适用场景

// ✅ 适合:兄弟组件、无直接关系的组件间通信// 如:Header 通知 Sidebar 刷新// ❌ 不适合:复杂全局状态(用 Pinia)// ❌ 不适合:父子通信(用 Props/Emit,更清晰)

五、Pinia / Vuex(全局状态)

5.1 何时使用

// 适合 Pinia 的场景:// 1. 用户登录态、Token、用户信息// 2. 购物车、收藏夹// 3. 应用全局配置(主题、语言、侧边栏状态)// 4. 多处页面共享的缓存数据

5.2 Pinia 基本用法

// stores/user.jsimport{defineStore}from'pinia'exportconstuseUserStore=defineStore('user',{state:()=>({name:'',token:''}),getters:{isLoggedIn:(state)=>!!state.token},actions:{login(token){this.token=token},logout(){this.token=''this.name=''}}})// 组件中使用import{useUserStore}from'@/stores/user'import{storeToRefs}from'pinia'constuserStore=useUserStore()const{name,isLoggedIn}=storeToRefs(userStore)// 保持响应性userStore.login('abc123')

5.3 与其他方式对比

方式数据范围持久化适用
Props/Emit父子局部数据
provide/inject组件树主题、上下文
mitt任意一次性通知
Pinia全局可插件持久化共享状态

六、组件注册方式

6.1 全局注册

import{createApp}from'vue'importAppfrom'./App.vue'importMyButtonfrom'./components/MyButton.vue'constapp=createApp(App)// 全局注册:任何模板中可直接使用app.component('MyButton',MyButton)app.mount('#app')
<!-- 任意组件模板中 --> <template> <MyButton>点击</MyButton> </template>

6.2 局部注册

<!-- 推荐:<script setup> 中导入即局部注册 --> <script setup> import MyButton from './MyButton.vue' import UserCard from './UserCard.vue' // 无需额外声明,导入即可在模板中使用 </script> <template> <MyButton /> <UserCard /> </template>

6.3 异步组件注册

import{defineAsyncComponent}from'vue'// 局部异步组件constHeavyModal=defineAsyncComponent(()=>import('./HeavyModal.vue'))// 全局异步注册app.component('HeavyModal',defineAsyncComponent(()=>import('./HeavyModal.vue')))// 带加载和错误状态constAsyncComp=defineAsyncComponent({loader:()=>import('./MyComponent.vue'),loadingComponent:LoadingSpinner,errorComponent:ErrorDisplay,delay:200,timeout:30000})

6.4 全局 vs 局部

对比项全局注册局部注册
使用范围任意组件当前组件
Tree-shaking不支持,未使用也会打包支持
依赖关系不明确清晰
适用基础通用组件(Button、Icon)业务页面组件
// 全局注册必须在 app.mount() 之前完成// <script setup> 导入的 .vue 文件自动局部注册// 组件名推荐 PascalCase,模板中可用 kebab-case

七、通信方式对比总结

父子直接通信 → Props / Emit 跨多层级透传 → provide / inject 兄弟/无关联组件 → mitt 或 Pinia 全局共享状态 → Pinia 基础 UI 组件 → 全局注册 业务页面组件 → 局部注册 + 异步加载

八、面试聚焦

8.1 Props 单向数据流

// 子组件不能直接修改 props// 应通过 emit 通知父组件修改emit('update:count',newValue)

8.2 provide/inject 响应式

// 默认不是响应式// 需要传递 ref 或 reactiveprovide('theme',ref('dark'))

8.3 全局注册无法 Tree-shaking

// 全局注册的组件即使未使用也会被打包// 业务组件应局部注册,支持 Tree-shaking

8.4 Vue 3 事件总线

// Vue 3 移除 $on/$off/$emit// 使用 mitt 库实现事件总线

九、易混淆点

  1. Props 是单向数据流:子组件不能直接修改 prop,应通过 emit 通知父组件。
  2. provide/inject 默认非响应式:传递refreactive才能实现响应式更新。
  3. mitt vs Pinia:mitt 适合一次性通知,Pinia 适合需要持久化的全局状态。
  4. 全局注册无法 Tree-shaking:未使用的全局组件仍会打包,业务组件应局部注册。
  5. defineProps / defineEmits:编译器宏,无需导入,不能在条件语句中使用。

十、思考与练习

1.Vue 组件通信有哪些方式?各自适用场景?

解析:

  • Props/Emit:父子直接通信
  • provide/inject:跨层级(主题、表单上下文)
  • mitt:兄弟或无关联组件
  • Pinia:全局共享状态

2.为什么子组件不能直接修改 props?

解析:Vue 遵循单向数据流,props 由父组件控制。子组件修改 props 会破坏数据流的可预测性,应通过 emit 通知父组件修改。

3.provide/inject 如何实现响应式?

解析:传递refreactive对象,而不是普通值:

provide('count',ref(0))// ✅ 响应式provide('count',0)// ❌ 非响应式

4.Vue 3 如何实现事件总线?

解析:使用 mitt 库替代 Vue 2 的$on/$off/$emit

importmittfrom'mitt'constbus=mitt()bus.on('event',handler)bus.emit('event',data)

5.全局注册和局部注册如何选择?

解析:

  • 全局注册:基础通用组件(Button、Input),减少重复导入
  • 局部注册:业务组件,依赖清晰,支持 Tree-shaking

总结

  • Props/Emit:父子通信,单向数据流,v-model 是其语法糖
  • provide/inject:跨层级通信,传递 ref/reactive 实现响应式
  • mitt:Vue 3 事件总线,替代o n / on/on/off
  • Pinia:全局状态管理,适合登录态、购物车等
  • 组件注册:全局(通用 UI)vs 局部(业务组件)vs 异步(按需加载)
http://www.gsyq.cn/news/1536879.html

相关文章:

  • AI Agent 评估:怎么判断你的智能体到底好不好用?
  • 2026年展台搭建公司TOP10推荐:高级创意/简约稳固/大型小型展位展台设计搭建匠心精选,标杆品牌与靠谱服务深度解析! - 品牌发掘
  • 终极指南:5分钟掌握Marketch,让Sketch设计秒变可测量网页
  • 2026最新B站字幕导出保姆级教程:手把手教你一键提取字幕
  • Gemini 3.5 Flash:架构级优化的本地大模型推理新范式
  • 北京4U机架工控机合规选型排行:5家实力服务商盘点 - 奔跑123
  • 河北双边护栏网厂家综合实力排行:5家头部厂商盘点 - 奔跑123
  • 御网杯wp回顾
  • 2026年国内高级工程师职称评审条件拆解,副高至正高层级进阶指南推荐 - 资讯焦点
  • 常州汽车隔音改装干货!针对本地路况降噪方案,彻底解决高速风噪胎噪 - 音乐人生汽车音响
  • 2026四川动画专业报考指南:学校怎么挑 - 品牌2026
  • 北京正规建账记账代理机构实力排行盘点 - 奔跑123
  • 【信息科学与工程学】【数据科学】第一百八十八篇 线性/非线性泛函分析01
  • __shfl_down_sync()用法理解
  • 2026年 展位设计公司推荐榜单:主题/特装/大面积及小面积展位设计搭建优质品牌盘点 - 品牌发掘
  • 如何快速掌握SHC脚本加密技术:面向初学者的完整指南
  • PMDARIMA股票预测:自动化ARIMA建模的工程实践指南
  • 邓柏良 “疏肝化瘀三联疗法”:中医治疗肝癌合并顽固性腹水的临床实践
  • 零基础拿捏交互式数据大屏!筛选器全局联动+蓝图数据流全流程爆肝详解
  • 洛雪音乐音源终极指南:三步搞定全网无损音乐自由
  • 2026年618史诗级降价潮来袭!6月17日20点京东准时开抢,最后28小时空调家电手机国补叠加全年较低价,错过再等一整年! - 资讯焦点
  • 2026年度宁波成人学历提升机构综合实力测评:三强揭晓,择校不踩雷
  • 田蜜蜜婚恋收费标准解析 会员分享签约体验 - 资讯焦点
  • 山东试压仪企业实力排行:5家核心厂商实测对比 - 奔跑123
  • 5分钟快速上手PoeCharm:流放之路玩家的免费中文角色构建终极指南
  • 如何快速配置MAA明日方舟智能助手:面向新手的完整教程
  • 旋风铣十大品牌实力盘点:腾创机械凭核心技术跻身前列 - 品牌推荐大师
  • 2026年苯酚产业链深度洞察:从源头到应用的原料格局与优质供应商解析 - 品牌发掘
  • Java CMS系统jspgou深度解析:从经典三层架构到现代化改造实战
  • 江岸区汽车贴膜避坑攻略!看完不再被套路 - 国麟测评