#javascript #vue.js #vuejs2
#javascript #vue.js #vuejs2
Вопрос:
У меня есть этот фрагмент кода VueJS:
new Vue({
el: '#app',
data: {
tiles: [
{ isActive: false },
{ isActive: false },
{ isActive: false },
{ isActive: false },
{ isActive: false }
]
},
methods: {
startWithLoop: function() {
console.log("startWithLoop");
for(var i = 0; i < 10000; i ) { this.blink() };
},
startWithInterval: function() {
console.log("startWithInteral");
setInterval(this.blink);
},
blink: function(){
console.log("blink");
var index = Math.floor(Math.random() * this.tiles.length);
this.tiles[index].isActive = !this.tiles[index].isActive;
}
}
})
Если я вызываю метод startWithInterval
, я могу видеть в представлении, как tiles
постоянно меняется состояние.
Если я вызываю метод startWithLoop
, я не вижу никаких изменений в представлении, пока цикл не будет завершен.
Вот JSFiddle
Как я могу инициировать изменения в представлении на каждом шаге цикла?
Комментарии:
1. Это связано с тем, что ваш цикл for поддерживает занятость основного потока: в этом случае у браузера нет времени между циклами для обновления DOM, поэтому он откладывает обновление до завершения цикла for.
2. Его задача со стеком, setInterval помещает функцию для вызова в стек, в то время как цикл for не делает его не асинхронным, он просто зависает до следующего нажатия
3. Вы могли бы использовать
$forceUpdate()
Ответ №1:
Нет, именно так Javascript eventloop работает в браузерах (и не только).
Вы можете себе представить, что Javascript выполняется только в «промежутках между моментами», так что ваша картина того, что происходит в браузере, является моментальным снимком момента.
Ответ №2:
Вы могли бы написать что-то похожее на цикл, которое setTimeout
позволяет Vue замечать изменения, а затем отправлять их в DOM.
beforeDestroy() {
if (this.timeout != null) {
clearTimeout(this.timeout);
this.timeout = null;
}
},
startWithLoop: function() {
console.log("startWithLoop");
let i = 0
const iter = () => {
this.blink();
i = 1;
if (i < 10000) {
this.timeout = setTimeout(iter);
}
}
iter();
},
Рабочая скрипка с изменениями, указанными выше: https://jsfiddle.net/ssorallen/9pqscat1/3 /
Комментарии:
1. Зачем так сильно усложнять цикл? разве этого недостаточно
for(var i = 0; i < 10000; i ) { setTimeout(this.blink) };
?2. Конечно, вы можете удалить это, если хотите. Я добавил это, потому что это было в вашем исходном коде.
3. Я хотел предложить упрощенную версию вашего кода, я был вынужден отредактировать ваш ответ, но подумал, что вам это может не понравиться. Я создал новый ответ, принимая ваше предложение, но упростив код, чтобы перейти непосредственно к делу. Спасибо
Ответ №3:
Подводя итог и объединяя все предложения, я понял, что:
- В середине цикла JS VueJS ничего не будет повторно отображать
Поэтому вам нужно переместить итерации вашего цикла в другой процесс. Я думал, что promises может быть решением, но проще использовать setTimeout()
без параметра delay .
Поэтому вместо этого:
for(var i = 0; i < 10000; i ) {
this.blink()
};
Мой код будет выглядеть так:
for(var i = 0; i < 10000; i ) {
setTimeout(this.blink)
}
Комментарии:
1. Это будет редко, но этот код может получить доступ к компоненту после его уничтожения. Если компонент
setTimeout
находится в очереди и уничтожен до истечения времени ожидания,this.blink
будет вызван для уничтоженного компонента. Скорее всего, это не вызовет проблем, но именно поэтому мой код включает в себяbeforeDestroy
код и, чтобы сохранить ссылку на время ожидания в очереди.