Vue 双向数据绑定

Flying
2018-03-06 / 0 评论 / 175 阅读 / 正在检测是否收录...

React 和 Vue 都主张组件之间交互用单向数据流,这样更 SOLID 的设计原则。但很多时候我们还是要用到双向数据流,也就是大家所说的 MVVM,一个很特殊的场景就是表单处理,使用双向数据绑定还是方便很多。和 Andular 一样,Vue 也可以通过 v-module 指令支持双向数据绑定。很多情况下,我们都需要自定义组件,以输入表单为例,怎样实现自定义组件双向数据绑定呢?

two-way-binding.svg

一、使用 props/$emit

该方法通过在组件中声明任意属性,再派发任意自定义事件来实现。接收组件实例还得绑定组件属性,编写自定义事件处理函数方法。可以参考工程代码中的 EventDemo.vue。本实例中,声明了 message 属性,再在 current 计算属性 set 方法中派发 update 事件。

  • 模板片段
<form>
  <fieldset>
    <legend>Event two-way binding</legend>
    <input 
      :value="message" 
      @click="$emit('update', $event.target.value)"
    >
  </fieldset>
</form>
  • 脚本片段
props: {
  message: {
    type: String,
    default: "",
  }
}
  • 调用示例
<event-demo 
  :message="message" 
  @update="message = $event" 
/>
这种方法最简单直接,不过调用时需要写较多代码。

二、使用自定义 v-model

该方法在 Vue 指南相关章节写得很清楚了。它其实是 props/$emit 方式的派生。对于输入表单组件,设置的属性名一定要是 value,派发的自定义事件名一定要是 input。可以参考工程代码中的 ValueDemo.vue。该实例中,我们为组件设置了 value 属性并派发了 input 事件。

  • 模板片段
<form>
  <fieldset>
    <legend>Value v-model</legend>
    <input :value="value" @input="$emit('input', $event.target.value)" />
  </fieldset>
</form>
  • 脚本片段
props: {
  value: {
    type: String,
    default: "",
  }
}
  • 调用示例
<value-demo v-model="message" />
<computed-demo v-model="message" />
<watch-demo v-model="message" />
注意:请记住!属性名和定义事件名按照约定的设定才能让自定义组件支持 v-model,不然就只是普通双向数据绑定了。虽然普通双向数据绑定不局限于属性名事件名更灵活一点,但用起来还是没 v-model 方便,对吧?

另外,工程代码提供了其它两个版本的自定义 v-model,还是那个原因:组件内部使用了 v-model,v-model 不能绑定 vue 属性,所以通过计算属性和 Watch 侦听器来实现。感兴趣的同学可以参考 ComputedDemo.vueWatchDemo.vue

注意:为什么不通过 change 事件方法来派发 update 事件呢?因为我们在组件内部使用了 v-model,v-model 不能绑定 props 属性。Eslint 会告警不能直接修改 props 属性,还有异步不能更新赋值的问题,感兴趣的同学可以参考< AsyncIssue.vue。如果改用 value 绑定 props 属性,再通过 change 事件方法来派发也是可以的。

三、使用自定义 sync 修饰符

从 Vue 1.0 开始,在组件实例属性上添加 sync 修饰符也可以实现自定义组件双向数据绑定。具体用法 Vue 指南相关章节介绍得很详细了。和自定义组件的 v-model 一样,它也只是 props/$emit 方式的派生。设置的属性名可以任意,派发的自定义事件一定要是 update:[属性名]。详细代码清单可以参考工程中的 SyncDemo.vue。该实例中,我们为组件设置了 message 属性并派发了 update:message 事件。

  • 模板片段
<form>
  <fieldset>
    <legend>Sync two-way binding</legend>
    <input v-model="current" />
  </fieldset>
</form>
  • 脚本片段
props: {
    message: {
      type: String,
      default: "",
    }
  },
  computed: {
    current: {
      get() {
        return this.message;
      },
      set(val) {
        this.$emit("update", val);
      }
    }
  }
注意:Element UI 的 el-dialog 组件就是一个通过自定义 visible 属性的 sync 修饰符来控制弹窗的显示隐藏的。

四、使用 props 对象

该方法需要在组件中声明对象属性,将对象属性绑定到输入表单的 value 属性,接着派发了 input 事件。它其实也是 props/$emit 方式的派生,适用于要双向数据绑定多个属性。详细代码清单可以参考工程中的 ObjectDemo.vue

注意:不建议直接将对象属性通过 v-model 绑定到输入表单上,v-model 双向绑定 props 后会修改它的值,这会导致不可意料的 Bug。Element UI 的 el-form 在外层将整个 props 对象绑定到 model 上,会将表单项的 prop 属性设定为相应的对象属性名,使用 v-model 双向绑定表单相应的对象属性值。这样设计很 nice,使用起来也很方便。

参考链接

6

评论 (0)

取消