Почему Vue не может отслеживать изменения длины массива?

внешний интерфейс JavaScript Vue.js

Официальный сайт объясняет следующее

Из-за ограничений JavaScript Vue не может обнаружить следующие изменяющиеся массивы: Когда вы устанавливаете элемент напрямую с помощью индекса, например: vm.items[indexOfItem] = newValue При изменении длины массива, например: vm.items.length = newLength

Потому что отзывчивость vue черезObject.definePropertyЧтобы добиться этого, свойство length массива не может добавлять геттеры и сеттеры, поэтому о нем нельзя судить, наблюдая за длиной.

пример

Следующий код, хотя кажется, что длина массива равна 10, но только 0, 1, 2 могут быть пройдены при for in, в результате чего только первые три индекса добавляются с помощью геттеров и сеттеров

var a = [0, 1, 2]
a.length = 10
// 只是显示的给length赋值,索引3-9的对应的value也会赋值undefined
// 但是索引3-9的key都是没有值的
// 我们可以用for-in打印,只会打印0,1,2
for (var key in a) {
  console.log(key) // 0,1,2
}

Затем vue предоставляет некоторые решения

Используйте встроенный Vue.$set

Пусть массив явно соблюдает определенный индекс Vue.set(массив, indexOfItem, новое значение)

на самом деле называется

Object.defineProperty(array, indexOfItem, {
  enumerable: true,
  configurable: true,
  get() { },
  set(newVal) { }
})

Таким образом, вы можете вручную указать ключ, который необходимо соблюдать, тогда можно будет добиться ожидаемого эффекта.

Переопределить методы push, pop, shift, unshift, splice, sort, reverse.

Исходный код Vue

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)

/**
 * Intercept mutating methods and emit events
 */
;[
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]
.forEach(function (method) {
  // cache original method
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (...args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    if (inserted) ob.observeArray(inserted)
    // notify change
    ob.dep.notify()
    return result
  })
})

Это методы, переопределенные или добавленные в Array.__proto__

И для метода добавления таких свойств, кактолкать, раскладывать, сращиватьДобавленные новые свойства проверяются вручную, исходный код

  if (inserted) ob.observeArray(inserted)

Ручной запуск сообщений для вышеуказанных методов

  ob.dep.notify()

В заключение

Vue не может напрямую наблюдать за прямым изменением длины массива, он предоставляет vue.$set для явного наблюдения и переписывает методы push, pop, shift, unshift, splice, sort, reverse для неявного наблюдения.