[路飞]_Vue的组件通信

446 阅读5分钟

本质

使需要通信的两个组件间可以对话

场景

父子组件通信

创建两个需要通信的页面,父页面为Parent,子页面为Child

  1. props/$emit
  • 父组件向子组件传参,在子组件中的props属性中定义需要传递的参数。

image.png

  • 在父组件引用子组件的实例中将title赋值。

image.png

  • 在子组件中想要通知父组件做事情,需要使用$emit的方式,在父组件中触发回调事件。

image.png

image.png

  1. $refs/ref
  • ref:给元素或子组件注册的引用
  • $refs:获取通过ref注册的引用 我们在Child子组件的引用中添加ref

image.png

通过$refs获取子组件

image.png

此时输出$refs我们可以看到,this.$refs获取的是一个组件的实例对象,而这个实例对象正是Child,Child中的data即方法等我们都可以通过this.$refs.child的方式来调用

image.png

父组件调用子组件中setMsg方法

image.png

image.png

image.png

当有多个子组件通过ref来声明时,this.$refs得到的是多个子组件对象,通过key来区分

image.png

image.png

当多个组件通过ref引用但key相同时,this.$refs所得到只是按子组件在父组件中放置的物理位置获取最后一个

image.png

image.png

ref还可以对元素进行引用

image.png image.png

  1. $parent/$children
  • $parent:获取子组件当前父组件的实例
  • $children:获取父组件当前引用的所有子组件实例 获取子组件

image.png

image.png image.png

可以通过数组下标找到你需要引用的子组件实例

image.png

通过this.$parent调用父组件实例中的focus方法,使父组件中的input聚焦

image.png

image.png

image.png

优缺点

  1. props/$emit
  • 推荐使用条件:通用性最强,依赖性最弱,没有受限使用条件
  • 特点:通用性最强,不依赖任何组件,props和$emit都是由组件本身定义,最为推荐的组件通信方式
  1. $refs/ref
  • 推荐使用条件:在需要调用元素上的方法或attribute时使用
  • 特点:在使用refs时必须在对应子组件引用中用ref定义,且必须要保证通过refs时必须在对应子组件引用中用ref定义,且必须要保证通过refs调用的参数或方法在对应的子组件中存在
  1. $parent/$children
  • 推荐使用条件:只有当你确定所有的子组件的应用顺序永远不会发生改变的时候可以使用
  • 特点:所获取的子组件的顺序受到物理放置位置的绝对影响,顺序依赖特别严重,如果放置顺序发生改变代码便会崩掉

多层级组件通信

image.png 如图中的组件A和到组件D即为多层级组件。 创建三个组件,形成A-->B-->C的爷孙嵌套关系,如果使用之前的组件通信方法,如使用props传值,那么就需要从组件A处向下冒泡,每一级都需要接收来自上一级的值,最后传递到C,层级少还好,层级多就会很繁琐,那么我们想要从A传递一个值直接一步到位C,可以使用provide(提供)/inject(接收)。

依赖注入provide(提供)/inject(接收)

image.png image.png

  1. provide提供值,参数 在A中通过provide定义title

image.png

在C中通过inject直接获取

image.png

image.png 2. provide提供对象 provide需要传递方法或组件对象时,需要将provide变为函数的形式。

image.png

在C中获取组件A的实例

image.png

image.png 有了这个之后,我们在C中就可以调用A中的方法

image.png

image.png

通过provide可以在父组件中获取子组件对象,如何在父组件中获取子组件对象呢?

image.png

image.png

image.png

依赖注入$attrs/$listeners

image.png

  1. attrsattrs `attrs`的作用就是props,当我们没有定义props的时候,组件传递的值我们依然可以获取到,就是通过$attrs获取。

image.png

image.png

但是当我们定义了props时再去调用$attrs时,$attrs就获取不到值了。

image.png

image.png

$attrs方式传值

A组件中定义attrs,attrs,attrs中存储的是有A的父级App组件传递过来的值

image.png

image.png

image.png

但是这种方法在传值的时候会存在将赋值的属性暴露在标签的属性中

image.png

这是因为在vue中,当你给组件传递一个值,而这个值不在props中的时候,会默认的挂载到当前的根节点上。解决这个问题可以将inheritAttrs属性设定为false。

image.png

image.png

所以attrs在传值上并没有很方便,那么attrs的主要使用场景是什么呢?attrs多用于自写组件的时候,比如我们想自定义写一个button按钮,当我们点击按钮想让该自定义按钮禁用,就可以利用attrs的特点,将disable属性透传到自定义组件中的button上

image.png

如果不通过attrs

image.png

image.png

利用attrs

image.png

image.png

  1. $listeners 用于侦听父组件在调用子组件时,在子组件上添加的事件监听。

image.png

image.png

image.png

非关系组件通信

image.png 非关系组件通信就好比两个异地得到人想要说话,需要打电话,打电话需要借助移动,电信等信号基站做中转。 这里我们用之前的组件A和C为例。在A中挂载一个方法init,在C中调用A中的init方法,借住vue的EventBus,订阅者模式。

image.png

创建一个vue的EventBus的js

image.png

在A中引入bus并挂载init事件

image.png

在C中调用A中的init事件

image.png

image.png

同样也可以传递参数

image.png

image.png

image.png 所以bus通信的本质就是不管组件之间有没有关联,相隔多远,都会进行组件间的通信。 我们可以通过bus.$off对挂载的事件进行移除。

image.png

image.png

这种移除使得init只被调用了一次,同样可以通过修饰符$once实现。

image.png

image.png 这种EventBus的实例方法在Vue3中就会被干掉,实现方法就变了,等讲到vue3的时候我们再说。除此之外这种EventBus的明显缺点是,过于灵活,通信指向性完全随机,不便于维护。所以在大型项目管理中,Vuex应运而生。

Vuex

是什么

Vuex是一个专门为Vue.js应用程序开发的状态管理模式,采用集中式存储应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

创建Vuex

Vuex跟vue-router类似,都属于插件,创建方式也如vue-router类似。在main.js中引用。

image.png

image.png

然后我们在任何组件中就可以通过this.this.store去调用。

image.png

image.png

但是我们在data中定义的变量直接用store赋值的话,当我们改变store对象的值是不会使页面的值发生更新的。

image.png

image.png

此时我们就需要用到计算属性,因为data中的属性值是依赖于store中的属性值。

image.png

image.png

但是如果属性比较多,在computed中就会显得很臃肿,所以vuex中给我们提供一个方法mapState专门针对属性管理的解决方法。通过数据解构的方式(...)就可以引用对应的属性。

image.png

但是如果当多个组件中都会对store.state的属性进行修改的话,如果某个修改存在问题,在查错时我们可能无法方便的去跟踪代码查错。

image.png 这时我们就需要在对store进行修改时,通过函数调用的方式,vuex中给我们提供了一个属性mutations,在mutations中定义各自state修改的方法,参数为vuex中需要修改的state,以及我们需要修改的目标值。

image.png

在调用的时候我们通过this.$store.commit(方法名,参数)的形式调用store中的修改方法,而不是让用户直接调用store中的方法,这样我们就把对stata的修改工作以及修改记录交给了commit,让用户只需要面对commit一个通道,我们在处理调用顺序时只需要交给commit处理就可以了,让程序变得更简单了。 image.png image.png

如果我们在多个页面需要显示一个人十年后的年龄,那么就都需要用到计算属性,在vuex中,我们可以定义全局计算属性getters,来完成。

image.png

image.png

但是vuex中的getters的全局计算属性的弊端就是,不再是缓存的。 commit中调用的方法都是同步执行的,当我们的计算属性方法是异步执行的,比如我们想先修改用户的名字在修改用户的年龄,在名字和年龄的修改中都用到异步的形式调用,我们让修改名字的函数延时5s,修改年龄的函数延时2s,这样就会与我们先修改名字再修改年龄的初衷相反,当我们需要写异步的时候,我们需要把异步处理的逻辑放到store中的actions中处理,然后再返回到commit中做同步处理就可以解决由于异步导致的不可预测的问题。 image.png

在actions中定义异步方法

image.png

在组件中调用,调用actions种方法时使用dispatch。dispatch返回的是一个promise,我们可以在一些等待处理。

image.png