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 of ref() and reactive() functions.
- there list goes on (e.g: store states in both
pinia and vuex, $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/vue@2.6.14"></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".