So, in Vue components, when you are writing tags in a template, you can specify a static parameter, like in a normal XML, or, you can specify a reactive parameter by adding colon in front of the parameter name.
Now, I stumbled upon this Vuetify example, and I played with the v-menu
component. This component has, for example, a close-on-click
property, which, in this example, is marked as reactive. That is logical, because in this example, this property is updated when you change the switch's position.
However, you can also specify a constant. And, since constant values do not change, in Vue, they do not have to be reactive. But, here, if I specify close-on-click
(without colon), it will not work properly. It will not read this property unless it is reactive.
My question is, why this property needs to be marked as reactive even if we specify a constant as its value?
So, to be more exact: Why <v-menu close-on-click=false
does not work, but <v-menu :close-on-click="false"
does?
false
is a constant, so, this property should not be marked as reactive.
CodePudding user response:
tldr:
Reactivity uses bindings to update the template, but not all bindings are necessarily reactive. This answer attempts to clarify the difference between these two related, but separate, concepts.
short answer:
It doesn't need to be "reactive". It needs to be "bound" (evaluated as JavaScript expression). Using...
<v-menu close-on-click="false" />
...binds the string "false"
to the property close-on-click
. Since the property expects a boolean, "false"
evaluates to true
, because any string other than empty string (""
) evaluates to true
when cast to boolean
. And...
<v-menu :close-on-click="false" />
binds the result of the JavaScript expression false
, which evaluates to false
, obviously.
This has nothing to do with reactivity. It has to do with evaluating the value passed to the attribute as string (without :
) or as a JavaScript expression (with :
).
long/proper answer:
You are mixing up two similar, but separate concepts.
One of them is "reactivity" and the other is "dynamically binding" through the use of a JavaScript expression.
The colon (:
) in front of an attribute is used for dynamically binding, not for reactivity.
:
is a shorthand for v-bind:
.
Think of binding as an alternative for {{ }}
(mustache) template binding, but designed for element attributes and props (because mustache doesn't work in attributes). Just as the mustache syntax, v-bind:
can be used to bind both reactive and non-reactive expressions to the template.
Non-reactive expressions will not update the template when the expression's result changes.
Reactivity, in Vue, is a term used to denominate Vue's "magical" ability to detect changes in data structures and update expressions using those data structures when a change has been detected.
A few examples of reactive objects:
- the object returned by
data()
function in Options API Vue.observable()
, in Vue 2.x- in Vue 3 (or when using
@vue/composition-api
plugin in Vue 2), the result ofref()
andreactive()
functions. - there list goes on (e.g: store states in both
pinia
andvuex
,$router
/$route
, etc...) but, under the hood, all reactive objects are an implementation of one of the cases above.
Effectively, reactivity is a wrapper. In Vue2, if you inspect a reactive object you will notice a [__ob__: Observer]
property on it. That's the reactivity observer.
In Vue3, it's no longer an Observer, but a Proxy.
The main difference is that in Vue3 the original object (the proxy's target) is virtually untouched. Also, change detection is more straightforward with Proxies.
If interested, there are a lot of materials online, typically labeled "advanced vue reactivity" or "vue reactivity in depth".
For a clearer understanding, here's an example which binds two expressions: one static and one reactive. Obviously, only the reactive one updates the template when its result changes:
const staticObj = { foo: 'bar' };
const reactiveObj = Vue.observable({ foo: 'bar' });
new Vue({
el: '#app',
computed: {
staticFoo() {
// won't update, because staticObj is static
return staticObj.foo;
},
reactiveFoo() {
// will update, because reactiveObj is reactive
return reactiveObj.foo;
}
},
methods: {
changeFoo() {
staticObj.foo = 'BAZ';
reactiveObj.foo = 'BAZ';
console.log('staticObj is now:', { ...staticObj }, ', but the template did not update!');
},
}
})
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
<div id="app">
Static foo: {{ staticFoo }}<br>
Reactive foo: {{ reactiveFoo }}
<br>
<button @click="changeFoo">Change foo</button>
</div>
Note: Even though I used {{}}
(mustache syntax) in the above example, the same thing happens when you use v-bind:
or its shorter version :
in element attributes.
I hope this clarifies the difference between "reactivity" and "binding".