Кровавый случай, вызванный массивом vue watch

внешний интерфейс JavaScript Vue.js
data () {
    return {
        nameList: ['jiang', 'ru', 'yi']
    }
},
methods: {
    handleClick () {
        // 通过push,unshift等方法改变数组可以通过watch监听到
        this.nameList.push('瑶')
        // 直接通过数组下标进行修改数组无法通过watch监听到
        this.nameList[2] = '爱'
        // 通过$set修改数组可以通过watch监听到
        this.$set(this.nameList, 2, '张')
        // 利用数组splice方法修改数组可以通过watch监听到
        this.nameList.splice(2, 1, '蒋如意')
    }
},
watch: {
    nameList (newVal) {
        console.log(newVal)
    }
}

Суммировать

Метод мутации
Vue включает в себя набор изменяющих методов, которые наблюдают за массивом, поэтому они также будут запускать обновления представления.Эти методы следующие:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

заменить массив
Методы мутации, как следует из названия, мутируют исходный массив, который был вызван этими методами. Напротив, существуют также неизменяющие методы, такие как filter(), concat() и slice(). Они не мутируют исходный массив, но всегда возвращают новый массив. При использовании неизменяющего метода вы можете заменить старый массив новым массивом.

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

// 方法一
Vue.set(vm.items, index, newValue)
Vue.splice(index, 1, newValue)

Для решения второго типа задач можно использовать сварку.

vm.items.splice(newLength)

Небольшое открытие: прямое изменение элементов массива с помощью подписки не может инициировать механизм рендеринга для обновления представления, но в это время значение массива изменилось.Если в то же время происходят другие изменения данных, когда вызывается повторный рендеринг, DOM, связанный с массивом, также будет обновлен для отображения последних данных.

Объяснить через представление vue

  1. Когда vue отслеживает данные, ему необходимо определить ключ свойства при инициализации данных и перехватить данные через Object.defineProperty, чтобы инициировать обновление представления при изменении данных, например:

obj: {
    name: '蒋',
    age: '28'
}

Два свойства name и age были определены с момента инициализации.В настоящее время можно отслеживать изменение значения двух свойств в obj и запускать обновление представления; Если к объекту obj через код js добавляется новое свойство, то при изменении этого свойства его нельзя будет отслеживать, если только новый объект не будет добавлен с помощью метода this.$set; 2. Массив также является объектом, а индекс эквивалентен ключевому значению свойства объекта, но когда Vue нацеливается на один массив, он не захватывает значение, соответствующее индексу, поэтому напрямую изменяет значение массива. элемент не может быть проконтролирован. и не может запускать обновления представлений, например:

arr1: [1, 2, 3, 4];
通过arr1[0] = 666,无法被监听到
arr2: [
    {
        name: 'a'
    },
    {
        name: 'b'
    }
]
arr2[0].name = 'cc';

Изменения в это время можно отслеживать и запускать обновления представления.

Мой вопрос:Почему vue не выполняет перехват данных на одном элементе массива?Протест может вызвать метод set путем перехвата данных

// 我的测试方式如下
------------- def开始 -----------------
function def (obj, key, val) {
  var value = val 
  Object.defineProperty(obj, key, {
    set (newVal) {
      console.log('触发set')
      value = newVal
    },
    get () {
      return value
    }
  })
}
-------------- def结束 ----------------
var arr = [1, 2, 3]

arr.forEach((item, index) => {
  def(arr, index, item)
})

arr[0] = 11
arr[1] = 22

console.log(arr) // [11, 22, 3]
-----------------------------
var obj = {
  list: ['a', 'b', 'c']
}
obj.list.forEach((item, index) => {
  def(obj.list, index, item)
})
obj.list[0] = 'jiang'
obj.list[1] = 'ru'
console.log(obj.list) // ['jiang', 'ru', 'c']

Объясните на уровне источника

// Из-за проблем с совместимостью браузера метод Object.observe не может отслеживать изменения данных, поэтому vue инкапсулирует класс Observe в процессе реализации.

  1. Значение, которое необходимо отслеживать, оценивается в методе конструктора класса Observer.
  2. Если значение является массивом, то вам нужно вызвать методObservArray для его обработки.
  3. МетодObserverArray в основном обходит каждый элемент массива и вызывает методObserver для обработки каждого элемента.
  4. Что делает метод наблюдения, так это то, что если элемент является простой строкой или числом, он ничего не делает и возвращается напрямую; если элемент является объектом, он вызываетсяnew Observer(value)метод для обработки элемента, возврата к первому классу и продолжения вниз; Из шага 4 вы можете узнать, почему изменение элементов массива по индексу не может вызвать обновление представления.
  5. Вернемся к Observer. Если будет установлено, что отслеживаемое значение не является массивом, вызовите метод walk для обработки элемента.
  6. Вызывается в методе ходьбыObject.keys()метод для перебора объекта и вызоваdefineReactive(obj, keys[i])метод
  7. Что делает метод defineReactive, так это использованиеObject.defineProperty()метод для прослушивания каждого свойства в объекте;
  8. будет вызываться в методе setdep.notify()метод, который должен уведомить наблюдателя о необходимости запуска метода обновления для повторного рендеринга представления;
  9. В методе get свойство будет добавлено к соответствующим зависимостям.

исходный код

// Из-за проблем с совместимостью браузераObject.observeМетод не может отслеживать изменения данных, поэтому vue инкапсулирует класс Observe в процессе реализации.

  1. Значение, которое необходимо отслеживать, оценивается в методе конструктора класса Observer.
  2. Если значение является массивом, то вам нужно вызвать методObservArray для его обработки.
  3. МетодObserverArray в основном обходит каждый элемент массива и вызывает методObserver для обработки каждого элемента.
  4. Что делает метод наблюдения, так это то, что если элемент представляет собой простую строку или число, он ничего не делает и возвращается напрямую; если элемент является объектом, он вызывает метод new Observer(value) для обработки элемента и возвращается к первому Класс продолжает снижаться;

5. Вернитесь к наблюдателю. Если будет установлено, что отслеживаемое значение не является массивом, вызовите метод walk для обработки элемента.
6. В методе walk вызывается метод Object.keys() для обхода объекта, а также вызывается метод defineReactive(obj, keys[i])

7. Что делает метод defineReactive, так это использует метод Object.defineProperty() для мониторинга каждого свойства в объекте;
8. В методе set будет вызываться метод dep.notify(), который должен уведомить наблюдателя о необходимости запуска метода обновления для повторного рендеринга представления;
9. Свойство будет добавлено к соответствующим зависимостям в методе get.

Как прослушать массив через часы

// 例一:一个简单的数组
data () {
    return {
        dataList: [1, 2, 3, 4]
    }
},
methods: {
    handleClick () {
        this.dataList.forEach((item, index) => {
            // 首先这里通过遍历数组改变元素的值,不能直接进行赋值更改,否则无法被监听到
            // item = '你好'
            // 需要用$set方法进行赋值
            this.$set(this.dataList, index, '你好')
        })
    }
},
watch: {
    dataList (newVal) {
        console.log(newVal) // ['你好', '你好', '你好', '你好']
    }
}

// 例二: 一个对象数组
data () {
    return {
        dataList: [
            {
                label: '一年级',
                status: '上课'
            },
            {
                label: '二年级',
                status: '上课'
            },
            {
                label: '三年级',
                status: '上课'
            },
            {
                label: '四年级',
                status: '上课'
            },
            {
                label: '五年级',
                status: '上课'
            },
            {
                label: '六年级',
                status: '上课'
            }
        ]
    }
},
methods: {
    handleClick () {
        // 如果是对象数组,可以通过这种方法改变元素的值,并且能够触发视图更行
        this.dataList.forEach(item => {
            item.status = '下课'
        })
    }
},
watch: {
    // dataList (newVal) { // 无法监听到数组变化
    //     newVal.forEach(item => {
    //        console.log(item.status)
    //     })
    //  },
    dataList: { // 通过设置deep的值可以监听到
        handler (newVal) {
            newVal.forEach(item => {
                console.log(item.status) // '下课', '下课', '下课', '下课', '下课', '下课'
            })
        },
        deep: true
    }
}

Из вышеприведенного примера можно узнать, что:

  1. Для одного простого массива, если вам нужно изменить значение элемента в нем, вам нужно изменить его с помощью метода this.$set, который можно отслеживать в это время и запускать обновление представления.
  2. Следует подчеркнуть, что если простой массив не изменяется с помощью метода this.$set, бесполезно, установлен ли в часах deep:true или нет, и изменения в массиве не могут быть отслежены.
  3. В примере 2 можно обнаружить, что в массиве объектов элементы в каждом объекте могут быть изменены напрямую и могут инициировать обновление представления, но если вам нужно отслеживать, изменяется ли массив с помощью наблюдения, вы должны добавить deep: true