Skip to content

Commit 5e289d7

Browse files
chrisvfritzyyx990803
authored andcommitted
Replace v-bind's .sync with a v-model argument (#8)
1 parent e91ee2b commit 5e289d7

File tree

1 file changed

+106
-0
lines changed

1 file changed

+106
-0
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
- Start Date: 2019-01-28
2+
- Target Major Version: 3.x
3+
- Reference Issues: https://github.com/vuejs/vue-next/issues/8
4+
- Implementation PR: (leave this empty)
5+
6+
## Summary
7+
8+
Removing `v-bind`'s `.sync` modifier and replacing it with an argument on `v-model`.
9+
10+
## Basic example
11+
12+
Instead of:
13+
14+
```vue
15+
<MyComponent v-bind:title.sync="title" />
16+
```
17+
18+
the syntax would be:
19+
20+
```vue
21+
<MyComponent v-model:title="title" />
22+
```
23+
24+
## Motivation
25+
26+
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:
27+
28+
> 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.
29+
30+
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?
31+
32+
## Detailed design
33+
34+
> **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 the `modelValue` attribute and `update:modelValue` event, know they are a placeholder for however we implement `v-model`'s special behavior with form elements and _not_ a recommendation included in this proposal.
35+
36+
### On an element
37+
38+
```vue
39+
<input v-model="xxx">
40+
41+
<!-- would be shorthand for: -->
42+
43+
<input
44+
:model-value="xxx"
45+
@update:model-value="newValue => { xxx = newValue }"
46+
>
47+
```
48+
49+
```vue
50+
<input v-model:aaa="xxx">
51+
52+
<!-- INVALID: should throw a compile time error -->
53+
```
54+
55+
Note that `v-bind:aaa.sync="xxx"` does _not_ currently throw a compile time error, though it probably should.
56+
57+
### On a component
58+
59+
```vue
60+
<MyComponent v-model="xxx" />
61+
62+
<!-- would be shorthand for: -->
63+
64+
<MyComponent
65+
:model-value="xxx"
66+
@update:model-value="newValue => { xxx = newValue }"
67+
/>
68+
```
69+
70+
```vue
71+
<MyComponent v-model:aaa="xxx"/>
72+
73+
<!-- would be shorthand for: -->
74+
75+
<MyComponent
76+
:aaa="xxx"
77+
@update:aaa="newValue => { xxx = newValue }"
78+
/>
79+
```
80+
81+
### Duplicating the object spread behavior of `v-bind.sync="xxx"`
82+
83+
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:
84+
85+
1. **Change the behavior of `v-model="xxx"` to spread an object, forcing users to write `v-model:model-value="xxx"` for the old behavior.** This would make `v-model`'s behavior more consistent with `v-bind` and `v-on`, but also create another breaking change and make the most common use case more verbose and complex.
86+
87+
2. **Add a new modifier (e.g. `.spread`) to `v-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.
88+
89+
3. **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, since `v-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 using `xxx` creates radically different behavior than `{ ...xxx }`.
90+
91+
4. **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 using `createElement`/`h`) in the worst case.
92+
93+
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.
94+
95+
## Drawbacks
96+
97+
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 addoption strategy below).
98+
99+
## Adoption strategy
100+
101+
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:
102+
103+
- Emit a warning when a `.sync` modifier is detected on `v-bind`, linking to this change's entry in the migration guide.
104+
- Using the new migration helper, we should be able to detect and automatically fix 100% of cases where `v-bind` is used with `.sync`.
105+
106+
Combined, learning about and migrating even large codebases with heavy `.sync` usage should take only a few minutes.

0 commit comments

Comments
 (0)