深入浅出,带你看懂Vue组件间通信的8种方案

简介: Vue种组件通信的情况有多种,总结有以下4种情况:• 父子组件间通信• 兄弟组件间通信• 祖孙后代间通信• 无关系组件间通信

前言


Vue种组件通信的情况有多种,总结有以下4种情况:

  • 父子组件间通信
  • 兄弟组件间通信
  • 祖孙后代间通信
  • 无关系组件间通信

8种解决方案

  1. 通过 props 传递
  2. 通过 $emit 触发自定义事件
  3. 使用 ref
  4. 使用 EventBus
  5. 使用 $parent 或$root
  6. 使用 attrs 与 listeners
  7. 使用 Provide 与 Inject
  8. 使用 Vuex

props进行组件间通信

网络异常,图片无法展示
|

Prop作为组件间通信的方式,并不是通用的,而是只能父子组件中使用。

场景:父组件传递数据给子组件

  • 子组件设置props属性种,接收父组件传递过来的参数
  • 父组件在使用子组件标签中通过字面量来传递值

具体什么样呢?

子组件:

props:{  
  // 字符串形式  
 name:String // 接收的类型参数  
  // 对象形式  
  age:{    
      type:Number, // 接收的类型为数值  
      defaule:18,  // 默认值为18  
     require:true // age属性必须传递  
  }  
 }

父组件:

<Children name="JanYork" age=18 />

这个很好理解,不懂的话直接上手试试。

$emit 触发

子组件通过$emit触发定义在父组件里面的自定义事件,他可以传两个值,第一个是自定义事件名,第二个是要传递的值。

  • 适用场景:子组件传递数据给父组件
  • 子组件通过$emit触发定义事件,$emit中可以携带两个参数('名字','参数')
  • 父组件绑定监听器获取到子组件传递过来的参数
//子组件
 this.$emit('msg', good)
//父组件中子组件
 <Children @msg="cartMsg($event)" />

使用ref获取

使用场景:ref 被用来给DOM元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。

  • 父组件在使用子组件的时候设置ref
  • 父组件通过设置子组件ref来获取数据

如果在Vue3中,那ref的作用就还有另一种了。

<Children ref="msg" />  
 this.$refs.msg

EventBus,即全局事件总线

  • 全局事件总线是一种组件间通信的方式,适用于任意组件间通信。
  • 相当于给每个组件做个代理,作为数据通信的中转站(可以理解为中间商)。
  • 其本质是Vue的实例对象,通过$emit、$on、$off发布、监听、关闭事件。
  • 一般放在Vue的原型对象上。

为什么要放到Vue的原型上呢???

看这样一张图(来自哔哩哔哩尚硅谷课堂)。

网络异常,图片无法展示
|

我们组件间通信是不是至少要两个组件,此时每个组件是不是有自己的实例对象,每个对象都有自己的原型对象对不对?(原型不懂没办法,JS没学好,可以去补课了)。

这张图是一个啥玩意呢?我稍微讲一讲。

JS基于对象的弱类型语言,所以JS的任何玩意,基本上都是对象。

此时我们Vue他是一个框架,也是JS写的,我们使用他时,必须要创建一个对象(也就是new Vue)。

这就是我们图的这一部分:

网络异常,图片无法展示
|

这个Vue的实例对象总是会经过原型链中的Vue原型对象。

我们组件也一样,Vue每个组件都有自己的实例对象,而每个实例对象都有自己的原型对象,而每个组件实例对象的原型总是要到达原型链中的Vue原型对象。

网络异常,图片无法展示
|

最后,因为Vue原型对象他仍然是一个对象,所以有会指向Object原型。

如果不懂,直接去看看尚硅谷的这堂课吧,讲的很好。

那此时我们这条原型链就清楚了,无论是Vue对象还是组件对象,还是组件实例的原型对象,都要经过Vue的原型对象

网络异常,图片无法展示
|

所以,这个Vue原型,它啥都可以接触到,那它当作中间商更合适不过了。

所以我们需要将这个全局事件总线(名字是$bus)挂载到原型:

// main.js
 import Vue from 'vue'
 import App from './App.vue'
 // 将$bus挂载在Vue原型上,当然也可挂载到Window上,不太建议
 Vue.prototype.$bus = new Vue()
 new Vue({
   render: h => h(App)
 }).$mount('#app')

但是有一个小问题,就是这里的Vue被new了两次,实际上是可以优化的。

// main.js
 import Vue from 'vue'
 import App from './App.vue'
 new Vue({
   render: h => h(App)
   beforeCreate () {
     //利用beforeCreate钩子函数挂载$bus,这是比较好的写法
     Vue.prototype.$bus = this
   }
 }).$mount('#app')

使用起来也很方便。

使用this.$bus.$emit发送事件,需要接受数据的组件用this.$bus.$on监听,当然不要忘了在beforeDestory钩子函数中,用this.$bus.$off解绑当前事件。

$off解绑单个事件this.$bus.$off('a'),多个可以用数组this.$bus.$off(['a', 'b'])

我演示一下:

网络异常,图片无法展示
|

不过我这个就是在同一个组件发送的消息,不同组件使用方法一样。

$parent 或 $root 实现兄弟组件通信

原理:通过共同的祖辈$parent或者$root,作为中间商,搭建通信桥连

兄弟组件1

this.$parent.on('msg',this.msg)

兄弟组件2

this.$parent.emit('msg')

就相当于,你哥哥有一个玩具,你想要,可能哥哥不给。

哥哥可能将玩具给了爸爸。

你去找爸爸要,爸爸给你了。

利用同一个祖辈来传递。

$attrs 与 $listeners 实现祖辈与子辈通信

用于祖先传递数据给子辈。

  • 设置批量向下传属性$attrs$listeners
  • 包含了父级作用域中不作为prop 被识别、获取的特性绑定 ( class 和 style 除外)。
  • 可以通过v-bind="$attrs" 传⼊内部组件

父组件调用子组件时,传递除了使用prop接收的属性以外 (class 和 style 除外),都可以使用$attrs获取。若要多层级组件使用 $attrs,则需要在中间子组件使用v-bind="$attrs" ,才可以被访问,否则访问$attrs为空对象。

不过——>在vue3.0中 $listeners被移除!!!

在 Vue 3 的虚拟 DOM 中,事件监听器现在只是以 on 为前缀的 attribute,这样就成了 $attrs 对象的一部分,因此 $listeners 被移除了。

在 Vue 2 中,你可以使用 this.$attrs 和 this.$listeners 分别访问传递给组件的 attribute 和事件监听器。结合 inheritAttrs: false,开发者可以将这些 attribute 和监听器应用到其它元素,而不是根元素。

实例(网上找了一个):

// child:并未在props中声明foo  
 <p>{{$attrs.foo}}</p>  
 // parent  
 <HelloWorld foo="foo"/>
// 给Grandson隔代传值,communication/index.vue  
 <Child2 msg="lalala" @some-event="onSomeEvent"></Child2>  
 // Child2做展开  
 <Grandson v-bind="$attrs" v-on="$listeners"></Grandson>  
 // Grandson使⽤  
 <div @click="$emit('some-event', 'msg from grandson')">  
 {{msg}}  
 </div>

provide 与 inject 实现

  • 在祖辈组件中定义provide属性,传递对应的值
  • 在子辈组件通过inject接收祖辈组件传递过来的值
//祖辈组件
 provide(){  
     return {  
         msg:'Hello World'  
     }  
 }
// 获取到祖辈组件传递过来的值  
 inject:['msg']

vuex实现全局通信

复杂关系的组件数据传递我们可以选则使用Vuex作为媒介,当然,你想的话,基本上任何数据都可以。

Vuex类似于一个存储数据的容器,而且是挂载全局的公用容器。

网络异常,图片无法展示
|

  • state用来存放共享变量的地方。
  • getter,可以增加一个getter派生状态,(相当于store中的计算属性),用来获得共享变量的值。
  • mutations用来存放修改state的方法。
  • actions也是用来存放修改state的方法,不过action是在mutations的基础上进行。常用来做一些异步操作。

总结


  • 父子关系的组件数据传递选择props$emit进行传递,也可选择ref
  • 兄弟关系的组件数据传递可选择$bus,其次可以选择$parent进行传递。
  • 祖先与后代组件数据传递可选择attrslisteners或者 ProvideInject
  • 复杂关系的组件数据传递可以通过vuex存放共享的变量。

扩展知识

PiniaVue.js轻量级状态管理库,最近很受欢迎。它使用 Vue 3 中的新反应系统来构建一个直观且完全类型化的状态管理库。

Pinia的成功可以归功于其管理存储数据的独特功能(可扩展性、存储模块组织、状态变化分组、多存储创建等)。

另一方面,Vuex也是为Vue框架建立的一个流行的状态管理库,它也是Vue核心团队推荐的状态管理库。 Vuex高度关注应用程序的可扩展性、开发人员的工效和信心。它基于与Redux相同的流量架构。

有兴趣的话,也可以试试Pinia

下次,来讲讲Pinia是否可以完美替代Vuex,以及VuexPinia的区别。

相关文章
|
26天前
|
JavaScript API 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
2天前
|
JavaScript 关系型数据库 MySQL
基于VUE的校园二手交易平台系统设计与实现毕业设计论文模板
基于Vue的校园二手交易平台是一款专为校园用户设计的在线交易系统,提供简洁高效、安全可靠的二手商品买卖环境。平台利用Vue框架的响应式数据绑定和组件化特性,实现用户友好的界面,方便商品浏览、发布与管理。该系统采用Node.js、MySQL及B/S架构,确保稳定性和多功能模块设计,涵盖管理员和用户功能模块,促进物品循环使用,降低开销,提升环保意识,助力绿色校园文化建设。
|
26天前
|
JavaScript 前端开发 开发者
Vue是如何劫持响应式对象的
Vue是如何劫持响应式对象的
24 1
|
26天前
|
JavaScript 前端开发 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
26天前
|
存储 JavaScript 前端开发
介绍一下Vue的核心功能
介绍一下Vue的核心功能
|
JavaScript 测试技术 容器
Vue2+VueRouter2+webpack 构建项目
1). 安装Node环境和npm包管理工具 检测版本 node -v npm -v 图1.png 2). 安装vue-cli(vue脚手架) npm install -g vue-cli --registry=https://registry.
1059 0
|
28天前
|
JavaScript 前端开发 开发者
vue 数据驱动视图
总之,Vue 数据驱动视图是一种先进的理念和技术,它为前端开发带来了巨大的便利和优势。通过理解和应用这一特性,开发者能够构建出更加动态、高效、用户体验良好的前端应用。在不断发展的前端领域中,数据驱动视图将继续发挥重要作用,推动着应用界面的不断创新和进化。
|
1月前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱前端的大一学生,专注于JavaScript与Vue,正向全栈进发。博客分享Vue学习心得、命令式与声明式编程对比、列表展示及计数器案例等。关注我,持续更新中!🎉🎉🎉
36 1
vue学习第一章
|
1月前
|
JavaScript 前端开发 索引
vue学习第三章
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中的v-bind指令,包括基本使用、动态绑定class及style等,希望能为你的前端学习之路提供帮助。持续关注,更多精彩内容即将呈现!🎉🎉🎉
26 1
vue学习第三章
|
1月前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
35 1
vue学习第四章
下一篇
DataWorks