Skip to content

用v-model参数替换v-bind sync修饰符

概要

移除v-bindsync修饰符,并用v-model替换

基本示例

而不是:

vue

<MyComponent v-bind:title.sync="title"/>

应该是:

vue

<MyComponent v-model:title="title"/>

动机

v-bind.sync在Vue2中造成了相当多的困惑,因为用户希望像v-bind那样使用它:

v-bind.sync视为具有额外行为的普通绑定是错误的,因为这与双向绑定有本质上的不同。.sync 修饰符的工作原理像v-model,它是Vue中另外一个支持双向绑定的语法糖。syncv-model主要的不同是前者支持双向绑定多个变量。

这不仅让我思考:既然告诉用户不要像v-bind那样思考v-bind.sync,又告诉用户它的原理像v-model ,那么为什么不使它成为v-model的一部分呢?

详细设计

注意:虽然这不是提案的异步,但是v-model的实现细节可能会在Vue3中改变,使透明包裹组件更容易实现。当你看到modelValue 属性和update:modelValue事件时,应该意识到这是支持表单元素上特殊行为的占位符。这并不在该RFC中。

在一个元素上

vue
<input v-model="xxx">

<!-- 是下面的简写 -->

<input
    :model-value="xxx"
    @update:model-value="newValue => { xxx = newValue }"
>
vue
<input v-model:aaa="xxx">

需要注意的是在当前版本使用v-bind:aaa.sync="xxx"并不会抛出一个编译时异常。

在一个组件上

vue

<MyComponent v-model="xxx"/>

<!-- 是下面的简写 -->

<MyComponent
    :model-value="xxx"
    @update:model-value="newValue => { xxx = newValue }"
/>
vue

<MyComponent v-model:aaa="xxx"/>

<!-- 是下面的简写 -->

<MyComponent
    :aaa="xxx"
    @update:aaa="newValue => { xxx = newValue }"
/>

复制v-bind.sync="xxx"传递对象的行为

其他两个带参数的指令是v-bindv-on,它们都支持传递对象给无参数版本,但是v-model 的无参数版本已经作为v-model:model-value="xxx"的简写。我认为有以下几种方案:

  1. 改变v-model="xxx"的行为(不再作为简写),强制要求用户通过v-model:model-value="xxx" 支持旧的行为。这可以与v-bindv-on的行为保持一致,但是也会造成另外一个重大改变,而且也会使经常使用的场景变得冗余和复杂。
  2. v-model新增一个修饰符(例如:.spread)。这是影响较小的变更,但是与另外两个指令的无参数版本传递对象的行为不一致,会隐式的造成困惑并且提高框架的复杂度。
  3. 监听传入的值,如果是对象则改变对应的行为。这也是影响较小的变更,但是可以与另外两个指令的行为保持一致,因为与v-bind="{ ...xxx }"具有相同的效果。
  4. 简化并且不允许传递对象到v-model。这可以避免上面提议中的问题,但是缺点是让一些用户更难迁移到Vue3(尽管可能是很小的一部分)。从v-bind.sync 中受益的Templates或者JSX变得编写和维护起来更加乏味(使用 createElement/h 强制重构渲染函数)。

并没有一个最好的观点,但是我更支持观点2,我也很想听听可能错过的其他解决方案。

缺点

除了重大变更造成的不可避免的痛苦外,我认为这个语法造成痛苦相对较小 - 部分原因是.sync并没有被广泛使用,也正因如此迁移起来相对容易。

采取的策略

作为一个重大变更,这只能在Vue3发布。然而,我认为我们可以做一些事件来使迁移更加容易:

  • 当检测到v-bind上的.sync修饰符时抛出一个警告,链接到迁移指南。
  • 使用一个新的迁移助手,我们应该尽可能的检测并且自动覆盖100%的v-bind.sync使用场景。

这些结合起来,学习以及迁移涉及到.sync的大型代码库应该只会花费几分钟。