原文链接 https://github.com/vuejs/rfcs/blob/master/active-rfcs/0011-v-model-api-change.md
- 开始时间:2019-04-09
- 目标主要版本: 3.x
- 引用 issue:vue/rfcs#8
- 实现的 PR:N/A
摘要
调整在自定义组件上使用 v-model API。
这建立在 #8(用 v-model 参数替换 v-bind 上的 .sync)之上。
基本范例
动机
在之前,组件上的 v-model=”foo” 大致可以编译为一下内容:
h(Comp, {value: foo,onInput: value => {foo = value}})
然而,这要求组件总是使用 value 属性来于 v-model 绑定,而组件可能想为不同目的暴露 value 属性。
在 2.2 中,我们引入了 model 组件选项,允许组件给 v-model 自定义 prop 和 event。然而,这仍然只允许在组件上使用 v-model。在实践中,我们看到很多组件需要多个同步值,而其他值必须使用 v-bind.sync。我们注意到 v-model 和 v-bind.sync 从根本上说是同一回事,可以允许 v-model 接受参数(如 #8 建议的那样)合并成一个结构。
具体设计
在 3.0 中,model 选项将会被移除。v-model=”foo”(不带参数)在一个组件上的编译结果如下:
h(Comp, {modelValue: foo,'onUpdate:modelValue': value => (foo = value)})
如果组件想要支持没有参数的 v-model,它应该期望一个名为 modelValue 的 prop。为了把它的值同步到父元素,子元素将会触发一个 “update:modelValue” 的事件(关于新的 VNode 数据结构的细节,请看 Render Function API change)。
默认的编译输出会在 prop 和 event 名称前加上 model,以避免于普通 prop 的名称冲突。
RFC #8 提出了 v-model 接受参数的能力。参数可以用来表示 v-model 应该绑定 prop。v-model:value=”foo” 应该编译成:
h(Comp, {value: foo,'onUpdate:value': value => (foo = value)})
在这种情况下,子组件期望一个 value 属性,并且触发 "update:value" 来进行同步。
注意,这可以在同一个组件上实现多个 v-model 绑定,每一个同步一个不同的 prop,而不需要在组件中设置额外的选项:
<InviteeFormv-model:name="inviteeName"v-model:email="inviteeEmail"/>
处理修饰符
在 2.x 中,我们对组件 v-model 上的 .trim 等修饰符进行了硬编码支持。然而,如果组件可以支持自定义的修饰符,那就更有用了。在 v3 中,添加到组件 v-model 的修饰符将通过 modelModifiers 属性提供给组件:
<Comp v-model.foo.bar="text" />
将会编译成:
h(Comp, {modelValue: text,'onUpdate:modelValue': value => (text = value),modelModifiers: {foo: true,bar: true}})
对于带有参数的 v-model,生成的 prop 名字将是 arg + “Modifiers”:
<Compv-model:foo.trim="text"v-model:bar.number="number" />
将会编译成:
h(Comp, {foo: text,'onUpdate:foo': value => (text = value),fooModifiers: { trim: true },bar: number,'onUpdate:bar': value => (bar = value),barModifiers: { number: true },})
使用原生元素
v-model 使用的另一个方面是在原生元素上。在 2.x 中,编译器会根据 v-model 使用的元素类型产生不同的代码。例如,它为 和 输出不同的属性和事件。然而,这种策略不能很好的处理动态元素和 input 类型:
<input :type="dynamicType" v-model="foo">
编译器没有办法在编译时猜测正确的属性和事件组合,所以它必须产生非常冗长的代码来涵盖所有有可能的情况。
在 3.0 中,v-model 在原生元素上产生的输出与使用组件时完全相同。例如, 编译为:
h('input', {modelValue: foo,'onUpdate:modelValue': value => {foo = value}})
然后,负责为 web 平台 patch 元素属性的模块将动态的决定如何实际使用它们。这使得编译器能够输出更少的冗长的代码。
缺点
TODO
备选方案
N/A
采纳策略
TODO
没有解决的问题
在 2.x 中,很难在原生自定义元素上使用 v-model,因为编译器无法区分原生自定义元素和普通的 Vue 组件(Vue.config.ignoredElements 仅在运行时)。其结果就是给定一个带有 v-model 的自定义元素。
<custom-input v-model="foo"></custom-input>
2.x 的编译器产生了 Vue 组件代码,而不是原生默认 value/input 对。
在 3.0中,编译器将为 Vue 组件和原生元素产生完全相同的代码,并且原生自定义组件将会作为原生元素被正确处理。
剩下的问题是,第三方自定义元素可能有未知的属性和事件组合,不一定遵循 Vue 的同步事件命名惯例。例如,如果一个自定义元素希望像复选框一样工作,Vue 就没有关于要绑定的属性或者监听的事件的信息。处理这个问题的一个可能的方法就是使用 type 特性作为一个提示:
<custom-input v-model="foo" type="checkbox"></custom-input>
这将告诉 Vue 使用与 <input type="checkbox"> 相同的逻辑来绑定v-model,使用 checked 作为属性,chang 作为事件。
如果自定义元素的行为不像任何现有的输入类型,那么最好使用显式的 v-bind 和 v-on 绑定。
