山地人

数据传递

山地人
山地人
2021-05-13

通过前面的学习,我们已经有了Vue使用的基本概念了。接下来,我们把注意力集中到数据的流转上。我们知道整个Vue搭建的应用,是建立在一课组件树上的。每个组件,都需要获得一些自己想要的数据,当内部发生变化时,可能需要让周围的一些组件状态也发生变化。

这样一来,就有了一下一些数据状态:

数据状态说明解决方法
无状态这种组件不依赖任何内部或外部的数据信息,不需要任何数据状态就能展示,属于纯展示类组件-
只有内部状态这种组件不依赖外部状态,数据状态的变化纯粹是组件内部的事情,内部的状态变化也不会影响外部组件的状态组件内data
只受外部数据状态的影响外部的数据状态发生变化,这个组件状态立即发生变化,没有内部数据的要求通过props中的属性获取外部数据的变化
只有内部状态,会影响外部组件状态这种组件自身的变化,依赖自己内部的数据状态,通过内部状态变化还会影响外部组件的状态变化组件内data+emit事件通知
依赖外部状态,同时影响外部组件状态这种组件自身变化依赖外部数据状态,同时也会影响外部主机状态如果是只依赖外部状态,则通过props数据解决,如果有部分依赖外部数据状态也有部分是内部数据状态,可以用props+data+emit事件结合的方式

下面我们通过一些例子,来了解不同情况的处理。

无状态

这种纯展示类的组件,最简单的纯表现类组件。如下面的这个hello组件。

只依赖内部状态

这个counter组件就是一个只依赖内部数据状态,通过又不受外部组件数据影响,也不会影响外部组件。所以只需要内部维护数据状态,使用data就可以解决问题。

只依赖外部状态

对于只依赖外部数据状态的组件,我们只需要提供props从组件外部获取到需要的数据即可。

外部和内部数据都有依赖

对于外部数据,通过props获取,对于内部数据状态,通过data获取。 下面这个counter是一个升级版的计时器,除了内部有计时功能,还会通过外部获取起始时间。

代码分析

  • 首先组件利用props声明了一个initCount的外部属性。
  • 在初始化data中的count时,利用了initCount作为了初始值
  • 之后,在mounted阶段,又初始化了一个定时器,不停更新count这个内部状态。

上面这个计时器,就是一个外部数据结合内部状态的例子。

内部状态影响外部

还有一些情况,组件内部的状态变化,会影响到组件外部的其他组件的状态的发生变化,这个时候,我们可以通过事件监听机制来解决。 或者,可以把外部的处理组件内部状态变化的处理函数,通过props属性传入到组件内部去调用,当然使用事件监听机制会是更好的做法。

下面我们分别观察两种做法的实现思路:

传入外部处理函数

这里,我们还是以计时器counter作为例子

代码分析

  • 这里通过给counter添加了一个接受外部函数的onChanged属性
  • 让后将app中的handleChange函数传入给counteronChanged
  • counter内部一旦发生count值的变化,立即调动onChanged对应的函数,从而达到通知外部组件更新的效果。

缺点

这种做法的缺点是:耦合度过高,我们需要在counter组件内判断外部是否传入了onChanged,对这段代码要做一个特殊的保护。

防止使用者没有设置onChanged导致函数调用异常。

因此,在这个基础上有出现了一种通过事件监听机制实现的方案。

事件机制的方案

代码分析

这个事件监听的方案和之前的传入回调函数方案相比,代码变化并不大。

  • app中的代码变化部分是:

    1. 传入给on-changed属性的方式改为给changed事件绑定handleChange处理函数。
    - <counter :on-changed="handleChange" />
    + <counter @changed="handleChange" />
  • counter中的代码变化部分为:

    1. 移除了onChanged属性声明
    2. 数据变化时,onChanged函数的调用改为发射changed事件。
    - props:["onChanged"],
    ...
    - if(this.onChanged){
    - this.onChanged(this.count)
    - }
    + this.$emit("changed",this.count)

这样处理,在counter内部不再关心要处理的函数是谁,它要做的就是在数据变化后,把这个事件或者说消息发送出去。 而对于外部关心这个数据的地方,可以通过监听消息,这里的设置changed事件处理函数的方式来进行。

$emit

  • 这里的$emit是Vue用来发送事件的API,"changed"是发送的事件名称,外部要接收这个事件可以用@changed来接收。
  • 后面的this.count是发送的消息时,传递出去的数据。会作为监听函数的参数传入监听函数中。
  • 当然@changed是一种简写,等价于v-on:changed

这是两种不同的思维方式。

依赖内外部状态同时影响外部状态

这种情况其实就是上述情况的综合,在处理上,我们依然可以使用上面一个案例的思路去处理。 外部数据通过props传入到组件内部,组件内部状态通过组件内部的data去处理,组件要影响外部的数据,可以通过外部传入的回调函数或者emit发送事件的方式通知外部变化。