Value as Attribute and Property with Vue.js

Attribute and property are confusing concepts in web development, and for value, because of its particularity, are more easy to puzzle us, I’m trying to distinguish them with examples

Concept of Attribute and Property

To put it simplely, attribute is for element tag, and property is for element object, such as:

1
2
3
4
5
6
<input id="input" value="test value">
<script>
let input = document.getElementById('input');
console.log(input.getAttribute('value')); // test value
console.log(input.value); // test value
</script>
  • Value attribute of input is defined through value="test value" of tag, and can be gotten via input.getAttribute('value'), and can be updated via input.setAttribute('value', 'New Value')
  • Value property of input can be gotten and updated via input.value,and the initial value is the same as attribute

Binding of Attribute and Property

At the very beginning, if the attribute value is updated, the property value will be changed too.

But if property value is updated (input in the input element or assign new value to input.value), the attribute value will not be changed, further more, if attribute value is updated again now, the property value will not be changed with it, such as this animation shown, and you can also try to operate in this page

Actually, the dirty value flag is taking effect. the initial value of dirty value flag is false, namely update of attribute value will be changed, but once property value is updated by user interaction, value of dirty value flag will become true, namely update of attribute value will not affect corresponding property value

So in the real world project, we’re generally handling value as property

Value handled by Vue.js

Usually use :value

v-bind of Vue.js, is handling attribute normally, if need to be handled as property, we need to add .prop

But v-bind:value are mostly handling property value by default, because it’s transformed forcibly

1
2
3
4
5
6
7
8
9
10
11
12
<input id="input" :value="'test value'" >
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let input = new Vue({
el: '#input',
mounted () {
console.log(this.$el.getAttribute('value')); // null
console.log(this.$el.value); // test value
console.log(this._vnode.data) // {attrs: {id: "input"}, domProps: {value: "test value"}}
}
});
</script>

Visible, Vue.js treat value as domProps of VNode’s data, not attrs, so it will become value as property after mounted

In the source code of Vue.js, the handling of forcibly transfer property is as following

1
2
3
4
5
6
7
8
9
10
// src/compiler/parser/index.js
function processAttrs (el) {
...
if ((modifiers && modifiers.prop) || (
!el.component && platformMustUseProp(el.tag, el.attrsMap.type, name)
)) {
addProp(el, name, value, list[i], isDynamic)
} else {
addAttr(el, name, value, list[i], isDynamic)
}

inside it, definition of platformMustUseProp in web platform is as following:

1
2
3
4
5
6
7
8
9
10
// src/platforms/web/util/attrs.js
const acceptValue = makeMap('input,textarea,option,select,progress')
export const mustUseProp = (tag: string, type: ?string, attr: string): boolean => {
return (
(attr === 'value' && acceptValue(tag)) && type !== 'button' ||
(attr === 'selected' && tag === 'option') ||
(attr === 'checked' && tag === 'input') ||
(attr === 'muted' && tag === 'video')
)
}

From the foregoing, value of input whose type is not button, textarea, option, select and progress will forcibly to be property, without the need to set :value.prop

For example, textarea tab does not support value attribute, so the value of following code will not shown in the textarea

1
<textarea value="test value"></textarea>

But in Vue.js, the following code can successfully bind to value property and show in the textarea

1
<textarea :value="'test value'"></textarea>

Explicitly use :value.prop

One more thing deserves attention in the above Vue.js source code is that, to be treated forcibly as property, !el.component need to be met, namely it’s not a dynamical component, because el.component of dynamical component will be its is attribute value

Namely v-bind of dynamical component will be treated as attribute, if need to be as property, the .prop need to be added, such as:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="app">
<component :is="element" :value.prop="'test value'"></component>
<button @click="change">Change</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
element: 'input'
},
methods: {
change () {
this.element = 'input' === this.element ? 'textarea' : 'input';
}
}
});
</script>

If in the above component, substitute :value.prop with .prop, when switch to textarea, the value will not show in the textarea, you can click to switch tab to see it in this page

Summary

  • The binding relationship of value as attribute and property will invalid after property value update by user interaction
  • Vue.js usually use :value to treat value as property
  • Dynamic component of Vue.js need to use :value.prop to treat value as property