简介

前端的一大重点是处理输入和输出,而 vue 的响应式系统给输入与输出的维护带来极大便利。将命令式操作转为响应式更新,我们不再需要为代码中充斥着大量手动的数据更新过程而头疼。

响应式的存在极大地简化了数据初始化和重新赋值的过程。通过定义计算属性或侦听器取代在 data 中定义普通属性,避免了组件加载时手动调用初始化函数以及依赖来源更新时手动重新更新依赖项。这使得我们在处理输入输出时,不必再额外关注数据的依赖关系,而是专心处理当前数据,可有效减少因遗漏了依赖的处理而导致的错误。

分析

定义响应式数据和处理逻辑

vue 中定义响应式有三种 API (仅负责当前组件内的数据,不包含组件通信相关的 props、$emit 等 API):

  • data:无依赖来源;可用于输出到视图或接受来自视图的输入
  • computed:依赖特定的数据,并在其值改变时自动更新当前属性;可用于输出到视图或接受来自视图的输入
  • watch:侦听特定的数据,并在其值改变时自动执行特定任务;不能与视图交互,只在本地数据之间建立关系

此外,methods 定义的方法本身不是响应式的,但若作为视图元素的事件处理回调时,可视作拥有一定程度的响应性。

各 API 的用途

data 可用于保存视图构建、后端响应相关的数据,前者仅用于输出到视图,后者可能既要输出又要接收输入。

计算属性有以下三大用途(来自官网的总结):

  • 取代复杂的模板内表达式,提高可读性。如: {{ message.split('').reverse().join('') }}
  • 取代部分方法,缓存对依赖项的计算结果,避免重复计算,提供性能。比如页面中多处引用方法会导致这些地方的模板多次计算并渲染,但若使用计算属性则不会重复计算。
  • 取代部分多依赖来源的 watch 侦听器,避免重复书写逻辑。若侦听器相同达到类似计算属性只在依赖改变时执行的效果,则需要手动加入条件判定。

个人总结的计算属性在数据处理中的主要用途是:当计算结果与依赖来源之间存在固定映射关系时,自动响应改变并更新结果。比如计算结果是依赖来源的某个属性、特定属性的组合或计算结果等

侦听器主要负责在单一依赖来源改变,且目标属性与被侦听属性并不具有特定映射关系的场景下。此外,若依赖改变时没有产生新的数据,也可以定义为侦听器。同时,侦听器还适合执行异步任务或大开销任务(其实也算异步任务,因为大开销本身就不适合作为同步任务)。

方法则主要用于数据的初始化,以及作为视图元素的事件回调。其内部可以涉及数据处理,也可以只包括交互行为的反馈。

不论方法、计算属性还是侦听器,这些 API 在数据处理中的目的都是为了从依赖的来源数据中得到新的数据。

各 API 的更新触发方式

data 只依赖于后端响应的数据,或者视图的输入,不依赖其他的本地数据。

计算属性依赖本地的数据变化,或者视图的输入。

侦听器依赖本地的数据变化。

方法由用户的交互行为触发或由其他方法调用。

在视图上应用响应式数据和处理逻辑

根据数据流向(输出、输入、输出与输入),可使用四种形式绑定数据和处理逻辑:

  • 只用于输出:v-bind
  • 只用于输入:
  • @event
  • v-model
  • 输入与输出(输出初始值,并根据输入更新)
  • v-bind + @event
  • v-model:本质还是 v-bind + @event,但是无法控制 @event
  • v-model + @event

其中,输入验证的处理可能有两类情况:

  • 对于返回新值的,可以通过 v-model 双向绑定,并在 change 或 input 事件的处理回调中,验证非法值并重置为特定值:此时,输入若为合法值的, v-model 会自动接收,无需额外赋值;或者可以通过 v-bind 绑定,然后同样在 change 或 input 事件中处理,但此时必须同时针对非法值和合法值进行额外的赋值操作,否则,合法值并没有被传给需要的变量。
  • 对于返回差异的,由于 v-model 的双向绑定赋值早于 change 事件,因此前一种方法是不行的,除非使用 input 事件;后一种方法仍然适用,因为赋值始终都是手动进行的,不存在获取不到旧值的情况。

此外,有一点需要注意:若后端对于用户的某些行为需要保存状态值时,可以在请求成功的回调里再去处理本地状态的赋值(此方法有些微的节流效果,但肯定不如真正的节流处理)。而此时亦不适合使用 v-model 绑定输入的更新值,因为赋值过程无法阻断。反而通过 v-bind 绑定,并在 change 事件的处理回调中更新值会更好。

简单总结

根据以上的分析,可以总结出以下几条规则,方便在处理数据时选择合适的 API:

  • 数据不存在后端响应或用户输入以外的本地依赖时,定义为 data 的属性即可,配合事件回调支持校验后赋值。
  • 属性与本地依赖来源之间为固定映射关系或接收用户输入,且无需校验的,可定义为计算属性。本身不支持异步更新,想要实现异步需要配合事件回调和 set 访问器更新依赖来源,随后才能触发本身的更新。
  • 属性与本地依赖来源之间不存在特定的映射关系,但在依赖改变时需要执行特定计算或其他过程的,可定义为侦听器。或者执行过程不涉及数据处理的,可定义为侦听器。尤其适合异步任务。
  • 由用户的非输入行为触发的,只能绑定事件处理方法。支持异步任务。

至于是通过 v-bind 还是 v-model 绑定,则根据输入是否需要经过特定的验证规则处理来确定,无需验证时直接使用 v-model 即可。需要校验时,可以根据需要绑定事件的处理方法。至于是选择 v-model 还是 v-bind,则根据更新值是否为接收的用户输入值本身以及更新时机确定。