vuejs падает, когда я использую v-for для итерации по итерируемой - PullRequest
2 голосов
/ 18 января 2020

У меня есть этот объект:

 const iterable = {
    items: [1,2,3,4],
    currentIndex: 0,
    [Symbol.iterator]() {
      const self = this;
      return {
        next() {
          return {
            done: self.currentIndex === self.items.length,
            value: self.items[self.currentIndex++]
          }
        }
      }
    }
  }

Я могу l oop, используя for of:

    for(let i of iterable){
        console.log(i)
    }

Теперь мне нужно повторить, используя v-for

<p v-for="item of iterable" :key="item">{{item}}</p>
* 1010. *

Этот запрос извлечения, Добавить итерируемые поддержки для v-for говорят, что эта функция была объединена и использована в vue 2.6.x. Я использую Vuejs 2.6.11.

1 Ответ

2 голосов
/ 18 января 2020

Вот пример использования 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...