vue组件v-model与sync修饰符

前言

在 vue 中我们可以通过 v-model 来实现很方便的双向绑定。那你知道 v-model 的原理吗?

其实 v-model 只是一个语法糖,Vue 会默认使用一个名为 value 的 prop,以及名为 input 的事件,即:

  • v-bind:绑定响应式数据
  • 触发 input 事件 并传递数据 (核心和重点)
1
<input v-model="inputValue" />

等价于:

1
2
3
4
<input
v-bind:value="inputValue"
v-on:input="inputValue = $event.target.value"
/>

既然我们知道了 v-model 的原理,那么我们在自定义组件中如何使用呢

自定义组件的 v-model

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。model 选项可以用来避免这样的冲突,在 2.2.0+ 版本中,vue 提供了一个 model 选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Vue.component("base-checkbox", {
model: {
prop: "checked",
event: "change"
},
props: {
checked: Boolean
},
template: `
<input
type="checkbox"
v-bind:checked="checked"
v-on:change="$emit('change', $event.target.checked)"
>
`
});

这样我们就把默认的 prop 改为 checked,将触发事件改为 change。

只要当子组件需要返回的值变动时,通过$emit方法触发 change 事件即可。这也是我们常用的子组件向父组件传递数据的方法。

.sync 修饰符

在 2.3+版本中,vue 提供了.sync 修饰符。

示例:

1
<comp :foo.sync="bar"></comp>

会被扩展为:

1
<comp :foo="bar" @update:foo="val => bar = val"></comp>

当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件:

1
this.$emit("update:foo", newValue);

这样看下来是不是跟 v-model 非常相似呢。

确实两者本质都是一样,并没有任何区别: “监听一个触发事件”=”(val) => value = val”。

细微之处的区别:

  1. 只不过 v-model 默认对应的是 input 或者 textarea 等组件的 input 事件,如果在子组件替换这个 input 事件,其本质和.sync 修饰符一模一样。比较单一,不能有多个。
  2. 一个组件可以多个属性用.sync 修饰符,可以同时”双向绑定多个“prop”,而并不像 v-model 那样,一个组件只能有一个。

总结功能作用场景:

  1. v-model 针对更多的是最终操作结果,是双向绑定的结果,是 value,是一种 change 操作。
    比如:输入框的值、多选框的 value 值列表、树结构最终绑定的 id 值列表(ant design 和 element 都是)、等等…
  2. .sync 针对更多的是各种各样的状态,是状态的互相传递,是 status,是一种 update 操作。
    比如:组件 loading 状态、子菜单和树结构展开列表(状态的一种)、某个表单组件内部验证状态、等等….