切换正在 vuejs 中监视的变量的值
toggle the value of variable that is being watched in vuejs
我有一个 typeof 为布尔值的数据变量,我已将它放在 watch hook 中。在将值更改为 true
时,我正在像这样在手表中执行一些任务并且它运行良好。
watch: {
boolVar: function (val) {
if(val === true){
//doing some tasks
}
}
}
然后我开始整合其他一些东西,要求是切换变量以防它已经为真,因为我想用观察者执行相同的任务。
假设,在某个点 boolVar
已经是 true
并且我必须再次 运行 观察者那我该怎么办?
以前,我是这样切换 boolVar
//in some other function
if(this.boolVar === true){
this.boolVar = false;
this.boolVar = true;
}
它应该可以工作,因为观察者将在值变化为真时监听并执行观察者中的功能。
但是,它并没有像我预期的那样工作。
因此,经过一些试验后,我了解到观察者正在侦听的时间延迟 difference/mismatch 这就是我的代码无法正常工作的原因。
然后我尝试了这种方法(通过使用睡眠和承诺方法,以便在 1 毫秒后观察者可以收听)并且它再次满足我的要求。
//in some other function
//now function is async to use await
if(this.boolVar === true){
this.boolVar = false;
await this.sleep(1);
this.boolVar = true;
}
//sleep function
sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
},
有什么最好的方法来处理这个问题吗?如果您想切换正在监视的变量并再次执行某些任务?因为目前,我的 hack 正在工作,但我认为 watcher 应该检测你何时切换相同的变量(正如我所期望的,但它没有检测到)
你应该使用 Vue.$nextTick.
if(this.boolVar === true){
this.boolVar = false;
this.$nextTick(() => {
this.boolVar = true;
})
}
如果你不想使用$nextTick,你可以这样做:
watch: {
boolVar: {
handler(val) {
console.log(val)
},
deep: true
}
}
但是,为什么不创建一个 doSomeWork() 函数,在您的观察器中调用它,当您需要重新执行该逻辑时,调用该函数而不是切换布尔值?这对我来说更有意义,但也许我遗漏了一些细节。 :
watch: {
boolVar: function (val) {
if(val === true){
this.doingSomeTasks()
}
}
}
并且:
if(this.boolVar === true){
this.doingSomeTasks()
}
您的代码没有按预期工作,因为您希望所有 watch
处理程序都同步执行,但事实并非如此。 Vue 正在使用 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.boolVar = false;this.boolVar = true;
- 两个更改都只是被 Vue“注册”了。当所有代码执行完毕后,“下一个事件循环”开始 - Vue 看到队列中的变化,找到注册的观察者并检查旧值(= 上次执行处理程序时的值)和新值。在你的情况下,两者都是 true
...所以从 Vue 的角度来看,没有变化,也不需要执行处理程序....
从技术 POV 来看,解决方案是将第二次更改 (this.boolVar = true;
) 推迟到稍后的“tick”,以便 Vue“看到”false
第一个,在下一个 tick 中它将 运行 观察者 (false
-> true
)
但是好的代码不仅是有效的代码,而且是传达程序员意图且易于理解的代码。 this.boolVar = false;this.boolVar = true;
严重违反了这个原则
当您的变量从 false
变为 true
时,您需要 运行 someTasks
。没关系。但是当变量已经是 true
时,您还希望在其他情况下 运行 someTasks
。很明显 someTasks
不应该在观察者中 而是按照@Julien 已经建议的那样提取到独立方法...
更新 ...只是为了完成
在 Vue 3 中,使用 watch 的新 flush
选项可以实现您想要的 - 只需使用值 sync
,您的观察者将在值发生变化时同步执行。请参阅下面的演示...
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>
我有一个 typeof 为布尔值的数据变量,我已将它放在 watch hook 中。在将值更改为 true
时,我正在像这样在手表中执行一些任务并且它运行良好。
watch: {
boolVar: function (val) {
if(val === true){
//doing some tasks
}
}
}
然后我开始整合其他一些东西,要求是切换变量以防它已经为真,因为我想用观察者执行相同的任务。
假设,在某个点 boolVar
已经是 true
并且我必须再次 运行 观察者那我该怎么办?
以前,我是这样切换 boolVar
//in some other function
if(this.boolVar === true){
this.boolVar = false;
this.boolVar = true;
}
它应该可以工作,因为观察者将在值变化为真时监听并执行观察者中的功能。 但是,它并没有像我预期的那样工作。
因此,经过一些试验后,我了解到观察者正在侦听的时间延迟 difference/mismatch 这就是我的代码无法正常工作的原因。
然后我尝试了这种方法(通过使用睡眠和承诺方法,以便在 1 毫秒后观察者可以收听)并且它再次满足我的要求。
//in some other function
//now function is async to use await
if(this.boolVar === true){
this.boolVar = false;
await this.sleep(1);
this.boolVar = true;
}
//sleep function
sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
},
有什么最好的方法来处理这个问题吗?如果您想切换正在监视的变量并再次执行某些任务?因为目前,我的 hack 正在工作,但我认为 watcher 应该检测你何时切换相同的变量(正如我所期望的,但它没有检测到)
你应该使用 Vue.$nextTick.
if(this.boolVar === true){
this.boolVar = false;
this.$nextTick(() => {
this.boolVar = true;
})
}
如果你不想使用$nextTick,你可以这样做:
watch: {
boolVar: {
handler(val) {
console.log(val)
},
deep: true
}
}
但是,为什么不创建一个 doSomeWork() 函数,在您的观察器中调用它,当您需要重新执行该逻辑时,调用该函数而不是切换布尔值?这对我来说更有意义,但也许我遗漏了一些细节。 :
watch: {
boolVar: function (val) {
if(val === true){
this.doingSomeTasks()
}
}
}
并且:
if(this.boolVar === true){
this.doingSomeTasks()
}
您的代码没有按预期工作,因为您希望所有 watch
处理程序都同步执行,但事实并非如此。 Vue 正在使用 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.boolVar = false;this.boolVar = true;
- 两个更改都只是被 Vue“注册”了。当所有代码执行完毕后,“下一个事件循环”开始 - Vue 看到队列中的变化,找到注册的观察者并检查旧值(= 上次执行处理程序时的值)和新值。在你的情况下,两者都是 true
...所以从 Vue 的角度来看,没有变化,也不需要执行处理程序....
从技术 POV 来看,解决方案是将第二次更改 (this.boolVar = true;
) 推迟到稍后的“tick”,以便 Vue“看到”false
第一个,在下一个 tick 中它将 运行 观察者 (false
-> true
)
但是好的代码不仅是有效的代码,而且是传达程序员意图且易于理解的代码。 this.boolVar = false;this.boolVar = true;
严重违反了这个原则
当您的变量从 false
变为 true
时,您需要 运行 someTasks
。没关系。但是当变量已经是 true
时,您还希望在其他情况下 运行 someTasks
。很明显 someTasks
不应该在观察者中 而是按照@Julien 已经建议的那样提取到独立方法...
更新 ...只是为了完成
在 Vue 3 中,使用 watch 的新 flush
选项可以实现您想要的 - 只需使用值 sync
,您的观察者将在值发生变化时同步执行。请参阅下面的演示...
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>