- Start Date: 2020-01-30
- Target Major Version: 3.x
- Reference Issues: #8731 #8735 #9397 #11053
- Implementation PR:
- Drop the internal concept of enumerated attributes and treat those attributes the same as normal non-boolean attributes.
- No longer removes attribute if value is boolean
false
. Instead, it's set asattr="false"
instead. To remove the attribute, usenull
orundefined
.
In 2.x, we have the following strategies for coercing v-bind
values:
-
For some attribute/element pairs, Vue is always using the corresponding IDL attribute (property): like
value
of<input>
,<select>
,<progress>
, etc. -
For “boolean attributes” and xlinks, Vue removes them if they are “falsy” (
undefined
,null
orfalse
) and adds them otherwise (see here and here). -
For “enumerated attributes” (currently
contenteditable
,draggable
andspellcheck
), Vue tries to coerce them to string (with special treatment forcontenteditable
for now, to fix vuejs/vue#9397). -
For other attributes, we remove “falsy” values (
undefined
,null
, orfalse
) and set other values as-is (see here).
In 2.x we modeled the concept of “enumerated attributes” as if they can only accept 'true'
or 'false'
, which is technically flawed. This also make them behave differently from other non-boolean attributes which led to confusion. The following table describes how Vue coerce “enumerated attributes” differently with normal non-boolean attributes:
Binding expr. | foo normal |
draggable enumerated |
---|---|---|
:attr="null" |
/ | draggable="false" |
:attr="undefined" |
/ | / |
:attr="true" |
foo="true" |
draggable="true" |
:attr="false" |
/ | draggable="false" |
:attr="0" |
foo="0" |
draggable="true" |
attr="" |
foo="" |
draggable="true" |
attr="foo" |
foo="foo" |
draggable="true" |
attr |
foo="" |
draggable="true" |
We can see from the table above, current implementation coerces true
to 'true'
but removes the attribute if it's false
. This also led to inconsistency and required users to manually coerce boolean values to string in very common use cases like aria-*
attributes like aria-selected
, aria-hidden
, etc.
-
We intend to drop this internal concept of “enumerated attributes” and treat them as normal non-boolean HTML attributes.
This solves the inconsistency between normal non-boolean attributes and “enumerated attributes”. It also made it possible to use value other than
'true'
and'false'
, or even keywords yet to come, for attributes likecontenteditable
. -
For non-boolean attributes, stop removing them if they are
false
and coerce them to'false'
instead.This solves the inconsistency between
true
andfalse
and makes outputingaria-*
attributes easier.
The following table describes the new behavior:
Binding expr. | foo normal |
draggable enumerated |
---|---|---|
:attr="null" |
/ | / † |
:attr="undefined" |
/ | / |
:attr="true" |
foo="true" |
draggable="true" |
:attr="false" |
foo="false" † |
draggable="false" |
:attr="0" |
foo="0" |
draggable="0" † |
attr="" |
foo="" |
draggable="" † |
attr="foo" |
foo="foo" |
draggable="foo" † |
attr |
foo="" |
draggable="" † |
†: changed
Coercion for boolean attributes is kept untouched.
This proposal is introducing the following breaking changes:
-
For “enumerated attributes”:
null
will remove the attribute instead of producing'false'
.- Numbers and string values other than
'true'
and'false'
won't be coerced to'true'
any more. (The old behavior shouldn't have been working anyway.)
-
For all non-boolean attributes,
false
values won't be removed any more and will be coerced to'false'
.
The most major breakage here is that users should stop relying on false
values to remove attributes, instead they are required to use null
or undefined
to do that. As boolean attributes are not affected, this change mostly affects enumerated attributes where 'false'
value and absence of the attribute result in different element state. eg. aria-checked
. It may also affect CSS selectors like [foo]
.
N/A
It's unlikely that a codemod can help in this case. We shall provide detailed information in our migration guide and document the coercion behavior in 3.x docs.
The absence of an enumerated attribute and attr="false"
may produce different IDL attribute values (which will reflect the actual state), described as follows:
Absent enumerated attr | IDL attr & value |
---|---|
contenteditable |
contentEditable → 'inherit' |
draggable |
draggable → false |
spellcheck |
spellcheck → true |
To keep the old behavior work, and as we will be coercing false
to 'false'
, in 3.x Vue developers need to make v-bind
expression resolve to false
or 'false'
for contenteditable
and spellcheck
.
In 2.x, invalid values were coerced to 'true'
for enumerated attributes. This was usually unintended and unlikely to be relied upon on a large scale. In 3.x true
or 'true'
should be explicitly specified.
In 3.x, null
or undefined
should be used to explicitly remove an attribute.
Attribute | v-bind value 2.x |
v-bind value 3.x |
HTML output |
---|---|---|---|
2.x “Enumerated attrs” i.e. contenteditable , draggable and spellcheck . |
undefined , false |
undefined , null |
removed |
true , 'true' , '' , 1 ,
'foo'
|
true , 'true' |
"true" |
|
null , 'false' |
false , 'false' |
"false" |
|
Other non-boolean attrs eg. aria-checked , tabindex , alt , etc. |
undefined , null , false |
undefined , null |
removed |
'false' |
false , 'false' |
"false" |
N/A