- Start Date: 2019-01-28
- Target Major Version: 3.x
- Reference Issues: vuejs/core#8
- Implementation PR: (leave this empty)
Removing v-bind
's .sync
modifier and replacing it with an argument on v-model
.
Instead of:
<MyComponent v-bind:title.sync="title" />
the syntax would be:
<MyComponent v-model:title="title" />
I've seen v-bind.sync
cause quite a bit of confusion in Vue 2, as users expect to be able to use expressions like with v-bind (despite whatever we put in the docs). The explanation I've had the best success with is:
Thinking about
v-bind:title.sync="title"
like a normal binding with extra behavior is really the wrong way to think about it, because two-way bindings are fundamentally different. The.sync
modifier works essentially like v-model, which is Vue's other syntax sugar for creating a two-way binding. The main difference is that it expands to a slightly different pattern that allows you to have multiple two-way bindings on a single component, rather than being limited to just one.
Which brings me to the question: if it helps to tell users not to think of v-bind.sync
like v-bind
, but rather to think about it like v-model
, should it be part of the v-model
API instead?
NOTE: Though not part of this proposal,
v-model
's implementation details are likely to change in Vue 3 to make common patterns like transparent wrapper components easier to implement. When you see themodelValue
attribute andupdate:modelValue
event, know they are a placeholder for however we implementv-model
's special behavior with form elements and not a recommendation included in this proposal.
<input v-model="xxx">
<!-- would be shorthand for: -->
<input
:model-value="xxx"
@update:model-value="newValue => { xxx = newValue }"
>
<input v-model:aaa="xxx">
<!-- INVALID: should throw a compile time error -->
Note that v-bind:aaa.sync="xxx"
does not currently throw a compile time error, though it probably should.
<MyComponent v-model="xxx" />
<!-- would be shorthand for: -->
<MyComponent
:model-value="xxx"
@update:model-value="newValue => { xxx = newValue }"
/>
<MyComponent v-model:aaa="xxx"/>
<!-- would be shorthand for: -->
<MyComponent
:aaa="xxx"
@update:aaa="newValue => { xxx = newValue }"
/>
The other directives with arguments are v-bind
and v-on
. Both of these use their argument-less versions for spreading an object, but v-model
without an argument is already shorthand v-model:model-value="xxx"
. I can see a few different options:
-
Change the behavior of
v-model="xxx"
to spread an object, forcing users to writev-model:model-value="xxx"
for the old behavior. This would makev-model
's behavior more consistent withv-bind
andv-on
, but also create another breaking change and make the most common use case more verbose and complex. -
Add a new modifier (e.g.
.spread
) tov-model
. This would minimize breaking changes, but is inconsistent with the object spread behavior of other directives with arguments, potentially causing confusion and making the framework feel more complex overall. -
Detect and change the behavior for raw object values (e.g.
v-model="{ ...xxx }"
). This would again minimize breaking changes, but is a little more consistent with the behavior of other directives with arguments, sincev-bind={ ...xxx }"
would have the same effect. I also expect this would be divisive, as some would likely find it very intuitive, while others would find it understandably confusing that usingxxx
creates radically different behavior than{ ...xxx }
. -
Simply don't allow object spread with
v-model
. This avoids the problems of the above two proposals, but has the drawback of making it more difficult for some people to migrate to Vue 3 (though probably a small minority). Templates/JSX that could benefit from the feature would also become much more tedious to write and maintain, in the best case, or unusable (forcing a refactor to a render function usingcreateElement
/h
) in the worst case.
None of these are great options, but I'm probably most in favor of option 2. I'd also love to hear suggestions for other solutions I may have missed.
Beyond the inevitable pains with any breaking change, I think the pain for this syntax would be relatively minimal - partly because the .sync
modifier is not as widely used a feature and also due to the potential easy of migrating users (see adoption strategy below).
As a breaking change, this could only be introduced in a major version change (v3). However, I think there are a few things we could do to make migrating easier:
- Emit a warning when a
.sync
modifier is detected onv-bind
, linking to this change's entry in the migration guide. - Using the new migration helper, we should be able to detect and automatically fix 100% of cases where
v-bind
is used with.sync
.
Combined, learning about and migrating even large codebases with heavy .sync
usage should take only a few minutes.