前言
Vue种组件通信的情况有多种,总结有以下4种情况:
- 父子组件间通信
- 兄弟组件间通信
- 祖孙后代间通信
- 无关系组件间通信
8种解决方案
- 通过 props 传递
- 通过 $emit 触发自定义事件
- 使用 ref
- 使用 EventBus
- 使用 $parent 或$root
- 使用 attrs 与 listeners
- 使用 Provide 与 Inject
- 使用 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
进行传递。 - 祖先与后代组件数据传递可选择
attrs
与listeners
或者Provide
与Inject
。 - 复杂关系的组件数据传递可以通过
vuex
存放共享的变量。
扩展知识
Pinia
是 Vue.js
的轻量级
状态管理库,最近很受欢迎。它使用 Vue 3 中的新反应系统来构建一个直观且完全类型化的状态管理库。
Pinia
的成功可以归功于其管理存储数据的独特功能(可扩展性、存储模块组织、状态变化分组、多存储创建等)。
另一方面,Vuex
也是为Vue
框架建立的一个流行的状态管理库,它也是Vue核心团队
推荐的状态管理库。 Vuex
高度关注应用程序的可扩展性
、开发人员的工效和信心。它基于与Redux
相同的流量架构。
有兴趣的话,也可以试试Pinia
。
下次,来讲讲Pinia
是否可以完美替代Vuex
,以及Vuex
与Pinia
的区别。