3đź‘Ť
You should use Vue.$nextTick.
if(this.boolVar === true){
this.boolVar = false;
this.$nextTick(() => {
this.boolVar = true;
})
}
If you don’t want to use $nextTick, you can do like that :
watch: {
boolVar: {
handler(val) {
console.log(val)
},
deep: true
}
}
However, why don’t you just make a doSomeWork() function, call it in your watcher, and when you need to re-execute that logic, you call the function rather than toggling your boolean? That would make more sense to me, but maybe I’m missing some details. :
watch: {
boolVar: function (val) {
if(val === true){
this.doingSomeTasks()
}
}
}
And :
if(this.boolVar === true){
this.doingSomeTasks()
}
1đź‘Ť
Your code does not work as you expect because you expect that all watch
handlers are executed synchronously but they are not. Vue is using Async Update Queue
Whenever a data change is observed, it will open a queue and buffer all the data changes that happen in the same event loop. If the same watcher is triggered multiple times, it will be pushed into the queue only once. This buffered de-duplication is important in avoiding unnecessary calculations and DOM manipulations. Then, in the next event loop “tick”, Vue flushes the queue and performs the actual (already de-duped) work.
This means that when you execute this.boolVar = false;this.boolVar = true;
– both changes are just "registered" by Vue. When all your code finished executing, "next event loop" starts – Vue sees the change in the queue, finds the registered watcher and checks the old (= value when the handler was last executed) and new value. Which are in your case both true
…so from the Vue point of view there is no change and no need to execute the handler….
From technical POV the solution is to postpone 2nd change (this.boolVar = true;
) to some later "tick" so Vue "sees" the false
1st and in the next tick it will run the watcher (false
-> true
)
But good code is not just the code that works but also the code that is communicating programmer’s intent and is easy to understand. this.boolVar = false;this.boolVar = true;
is violating this principle in a big way
You need to run someTasks
when your variable changes from false
to true
. That’s fine. But you also want to run someTasks
in other scenario when the variable is already true
. It is clear that someTasks
should not be in the watcher but extracted to independent method as @Julien already suggested…
Update …just to be complete
What you want is possible in Vue 3 with the new flush
option of the watch – just use value sync
and your watcher will be executed synchronously anytime the value changes. See the demo below…
const app = Vue.createApp({
data() {
return {
value: true,
flushMode: 'sync',
unwatch: null
}
},
methods: {
toggle() {
this.value = false
this.value = true
}
},
watch: {
flushMode: {
handler: function() {
this.unwatch && this.unwatch()
this.unwatch = this.$watch('value',
function(newValue) {
console.log(`New value = ${newValue}`)
},
{ flush: this.flushMode }
)
},
immediate: true
}
},
updated() {
console.log('Component updated')
}
})
app.mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.1.1/vue.global.prod.min.js" integrity="sha512-vRIbP8hGYYKnayuLhVRwQXTxvAJ/wUm4vqIEzIBKLfDcM2ArSENuojG1jEIactQ7F/0unOe9VQ+U9KagTEyQvA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="app">
<div>
Variable: {{ value }}
</div>
<button @click="toggle">Toggle</button>
<div>
<label for="pre">Flush mode:</lael>
<input type="radio" id="pre" value="pre" v-model="flushMode" /> pre (default)
<input type="radio" id="sync" value="sync" v-model="flushMode" /> sync
</div>
</div>