Вот пример использования v-for
с итерацией:
const iterable = {
items: [1, 2, 3, 4],
[Symbol.iterator] () {
const items = this.items
const length = items.length
let currentIndex = -1
return {
next () {
currentIndex++
return {
done: currentIndex >= length,
value: items[currentIndex]
}
}
}
}
}
new Vue({
el: '#app',
data () {
return { iterable }
},
methods: {
onAddClick () {
this.iterable.items.push(Date.now())
},
onRemoveClick () {
this.iterable.items.pop()
}
}
})
<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
<div id="app">
<p v-for="item of iterable">{{ item }}</p>
<button @click="onAddClick">Add item</button>
<button @click="onRemoveClick">Remove item</button>
</div>
Основная проблема с исходным кодом заключается в том, что он не сбрасывает счетчик при создании нового итератора. Так что в первый раз, когда вы пройдете через итерацию, все будет работать нормально. Но во второй раз счетчик уже находится в конце массива. Проверка для self.currentIndex === self.items.length
всегда будет false
, так как currentIndex
будет больше, чем length
.
Вы можете увидеть ту же проблему, просто используя for
/ of
l oop. Например:
for(let i of iterable){
console.log(i)
}
for(let i of iterable){
console.log(i)
}
Первый l oop будет работать без проблем, но второй l oop никогда не закончится.
Почему Vue пытается l oop через повторяемую дважды ...
Я предполагаю, что вы выставляете iterable
через свойство data
. currentIndex
будет реагирующим и будет зарегистрирован как зависимость рендеринга. По мере увеличения он запускает повторный рендеринг. Теоретически вы попадете в бесконечную рекурсию рендеринга, но на практике она не выйдет за пределы второго рендеринга из-за бесконечной итерации l oop.
Ключом к исправлению этого является сохранение currentIndex
ограничено итератором и не повторяется. Есть несколько способов реализовать это, но вот тот, который я использовал в моем предыдущем примере:
const iterable = {
items: [1, 2, 3, 4],
[Symbol.iterator] () {
const items = this.items
const length = items.length
let currentIndex = -1
return {
next () {
currentIndex++
return {
done: currentIndex >= length,
value: items[currentIndex]
}
}
}
}
}
Здесь currentIndex
просто содержится в закрытии метода next
. В качестве альтернативы его можно добавить как свойство объекта итератора вместе с next
.