Vue 数据在 setInterval() 方法中更新,但 dom/view 不更新
Vue data updates in method with setInterval() but the dom/view doesn't update
在我的方法中,我有一个在视图中使用@click 触发的函数。
此函数启动一个计时器,计时器的工作方式与我在 DevTools 中看到的一样,但它仅在我点击刷新时在 DevTools 中更新。
此外,在视图中,计时器根本没有显示,因为它只呈现一次,而且它不寻找更新。
更新:
应要求提供完整代码
<template>
<div class="container-fluid">
<div class="card mt-2 p-3 w-75 mx-auto">
<h2>League Summoners Timers</h2>
<hr>
<div>
<h5>Enemy team</h5>
<div class="row">
<div class="col-md-12 col-lg-2" v-for="index in lanes" :key="index">
<div class="card">
<div class="card-header">
<p class="m-0">{{ index }}</p>
</div>
<div class="card-body">
<div v-show="!gameStarted">
<div v-show="selected[index.toLowerCase()].length < 2" class="spell-image-row" v-for="spell in spells" :key="spell.name" @click="onClickSelection(spell, index)">
<img class="spell-image m-1" :alt="spell.name" :src="spell.image">
</div>
<div v-show="selected[index.toLowerCase()].length >= 2">
<span class="alert alert-primary">Spells selected</span>
<ul class="mt-3">
<li v-for="selectedSpell in selected[index.toLowerCase()]" :key="selectedSpell">
{{ selectedSpell }}
</li>
</ul>
</div>
</div>
<div v-show="gameStarted">
<div v-for="spell in selected[index.toLowerCase()]" :key="index+spell">
<div class="row">
<img style="float: left" class="my-1" :src="spells[spell.toLowerCase()].image" :alt="spell.name" @click="onClickStartTimer(spell, index)">
<p>{{ timers[index.toLowerCase()][spell.toLowerCase()] }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="mt-3">
<button class="btn btn-primary mr-3" @click="gameStarted = true" v-show="checkSelected() && !gameStarted">Start game</button>
<button class="btn btn-danger" onClick="window.location.reload();">Reset</button>
</div>
</div>
</div>
<script>
export default {
name: "LeagueTimers",
data() {
return {
gameStarted: false,
lanes: ['Top', 'Jungle', 'Mid', 'ADC', 'Support'],
spells: {
teleport: {
name: 'Teleport',
image: require('../assets/images/Teleport.png'),
cooldown: 420,
},
ignite: {
name: 'Ignite',
image: require('../assets/images/Ignite.png'),
cooldown: 180,
},
heal: {
name: 'Heal',
image: require('../assets/images/Heal.png'),
cooldown: 240,
},
ghost: {
name: 'Ghost',
image: require('../assets/images/Ghost.png'),
cooldown: 300,
},
flash: {
name: 'Flash',
image: require('../assets/images/Flash.png'),
cooldown: 300,
},
exhaust: {
name: 'Exhaust',
image: require('../assets/images/Exhaust.png'),
cooldown: 210,
},
cleanse: {
name: 'Cleanse',
image: require('../assets/images/Cleanse.png'),
cooldown: 210,
},
barrier: {
name: 'Barrier',
image: require('../assets/images/Barrier.png'),
cooldown: 180,
},
smite: {
name: 'Smite',
image: require('../assets/images/Smite.png'),
cooldown: 15,
},
},
selected: {
top: [],
jungle: [],
mid: [],
adc: [],
support: [],
},
timers: {
top: {},
jungle: {},
mid: {},
adc: {},
support: {},
},
}
},
methods: {
onClickSelection(spell, lane) {
this.selected[lane.toLowerCase()].push(spell.name);
},
onClickStartTimer(spell, lane) {
if (!this.timers[lane.toLowerCase()][spell.toLowerCase()]) {
this.startTimer(spell.toLowerCase(), lane.toLowerCase(), this.spells[spell.toLowerCase()].cooldown);
} else {
console.log('runt al');
}
},
checkSelected() {
for (let [key] of Object.entries(this.selected)) {
if (this.selected[key].length !== 2) {
return false;
}
}
return true;
},
startTimer(spell, lane, cooldown) {
this.timers[lane][spell] = cooldown;
let timers = this.timers;
setInterval(function (){
timers[lane][spell]--;
// console.log(lane+spell + " - " + timers[lane][spell]);
// console.log(typeof timers[lane][spell])
this.$forceUpdate();
}, 1000);
},
},
}
</script>
到目前为止,我尝试过使用 watch: {} 和 computed: {},但到目前为止没有任何效果。我必须补充一点,我是 Vue 的新手。
尝试以这种方式声明您的 setInterval 函数
setInterval(() => {
this.timers[lane][spell]--;
console.log(lane+spell + " - " + timers[lane][spell]);
console.log(typeof timers[lane][spell])
// this.$forceUpdate();
}, 1000);
使用箭头函数,您可以使用 this.timers 访问属性数据。
使用Function语法会创建一个新的作用域,this.timers不可用
如果想使用函数语法,可以通过这种方式绑定this对象
setInterval(function () {
this.timers[lane][spell]--;
console.log(lane+spell + " - " + timers[lane][spell]);
console.log(typeof timers[lane][spell])
}.bind(this), 1000)
此外,要为数据 属性 计时器内的对象添加反应性,您必须首先声明它们:
timers:
{ top:
{ teleport: 0,
smite: 0,
... },
...
}
如果你想动态添加这些属性,vue set 属性 是实现它的方法,你可以在这里阅读更多相关信息 https://vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
在我的方法中,我有一个在视图中使用@click 触发的函数。 此函数启动一个计时器,计时器的工作方式与我在 DevTools 中看到的一样,但它仅在我点击刷新时在 DevTools 中更新。
此外,在视图中,计时器根本没有显示,因为它只呈现一次,而且它不寻找更新。
更新: 应要求提供完整代码
<template>
<div class="container-fluid">
<div class="card mt-2 p-3 w-75 mx-auto">
<h2>League Summoners Timers</h2>
<hr>
<div>
<h5>Enemy team</h5>
<div class="row">
<div class="col-md-12 col-lg-2" v-for="index in lanes" :key="index">
<div class="card">
<div class="card-header">
<p class="m-0">{{ index }}</p>
</div>
<div class="card-body">
<div v-show="!gameStarted">
<div v-show="selected[index.toLowerCase()].length < 2" class="spell-image-row" v-for="spell in spells" :key="spell.name" @click="onClickSelection(spell, index)">
<img class="spell-image m-1" :alt="spell.name" :src="spell.image">
</div>
<div v-show="selected[index.toLowerCase()].length >= 2">
<span class="alert alert-primary">Spells selected</span>
<ul class="mt-3">
<li v-for="selectedSpell in selected[index.toLowerCase()]" :key="selectedSpell">
{{ selectedSpell }}
</li>
</ul>
</div>
</div>
<div v-show="gameStarted">
<div v-for="spell in selected[index.toLowerCase()]" :key="index+spell">
<div class="row">
<img style="float: left" class="my-1" :src="spells[spell.toLowerCase()].image" :alt="spell.name" @click="onClickStartTimer(spell, index)">
<p>{{ timers[index.toLowerCase()][spell.toLowerCase()] }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="mt-3">
<button class="btn btn-primary mr-3" @click="gameStarted = true" v-show="checkSelected() && !gameStarted">Start game</button>
<button class="btn btn-danger" onClick="window.location.reload();">Reset</button>
</div>
</div>
</div>
<script>
export default {
name: "LeagueTimers",
data() {
return {
gameStarted: false,
lanes: ['Top', 'Jungle', 'Mid', 'ADC', 'Support'],
spells: {
teleport: {
name: 'Teleport',
image: require('../assets/images/Teleport.png'),
cooldown: 420,
},
ignite: {
name: 'Ignite',
image: require('../assets/images/Ignite.png'),
cooldown: 180,
},
heal: {
name: 'Heal',
image: require('../assets/images/Heal.png'),
cooldown: 240,
},
ghost: {
name: 'Ghost',
image: require('../assets/images/Ghost.png'),
cooldown: 300,
},
flash: {
name: 'Flash',
image: require('../assets/images/Flash.png'),
cooldown: 300,
},
exhaust: {
name: 'Exhaust',
image: require('../assets/images/Exhaust.png'),
cooldown: 210,
},
cleanse: {
name: 'Cleanse',
image: require('../assets/images/Cleanse.png'),
cooldown: 210,
},
barrier: {
name: 'Barrier',
image: require('../assets/images/Barrier.png'),
cooldown: 180,
},
smite: {
name: 'Smite',
image: require('../assets/images/Smite.png'),
cooldown: 15,
},
},
selected: {
top: [],
jungle: [],
mid: [],
adc: [],
support: [],
},
timers: {
top: {},
jungle: {},
mid: {},
adc: {},
support: {},
},
}
},
methods: {
onClickSelection(spell, lane) {
this.selected[lane.toLowerCase()].push(spell.name);
},
onClickStartTimer(spell, lane) {
if (!this.timers[lane.toLowerCase()][spell.toLowerCase()]) {
this.startTimer(spell.toLowerCase(), lane.toLowerCase(), this.spells[spell.toLowerCase()].cooldown);
} else {
console.log('runt al');
}
},
checkSelected() {
for (let [key] of Object.entries(this.selected)) {
if (this.selected[key].length !== 2) {
return false;
}
}
return true;
},
startTimer(spell, lane, cooldown) {
this.timers[lane][spell] = cooldown;
let timers = this.timers;
setInterval(function (){
timers[lane][spell]--;
// console.log(lane+spell + " - " + timers[lane][spell]);
// console.log(typeof timers[lane][spell])
this.$forceUpdate();
}, 1000);
},
},
}
</script>
到目前为止,我尝试过使用 watch: {} 和 computed: {},但到目前为止没有任何效果。我必须补充一点,我是 Vue 的新手。
尝试以这种方式声明您的 setInterval 函数
setInterval(() => {
this.timers[lane][spell]--;
console.log(lane+spell + " - " + timers[lane][spell]);
console.log(typeof timers[lane][spell])
// this.$forceUpdate();
}, 1000);
使用箭头函数,您可以使用 this.timers 访问属性数据。 使用Function语法会创建一个新的作用域,this.timers不可用
如果想使用函数语法,可以通过这种方式绑定this对象
setInterval(function () {
this.timers[lane][spell]--;
console.log(lane+spell + " - " + timers[lane][spell]);
console.log(typeof timers[lane][spell])
}.bind(this), 1000)
此外,要为数据 属性 计时器内的对象添加反应性,您必须首先声明它们:
timers:
{ top:
{ teleport: 0,
smite: 0,
... },
...
}
如果你想动态添加这些属性,vue set 属性 是实现它的方法,你可以在这里阅读更多相关信息 https://vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats